Repository: Masterminds/squirrel Branch: master Commit: 1ded5784535d Files: 48 Total size: 134.4 KB Directory structure: gitextract_422pidv7/ ├── .gitignore ├── .travis.yml ├── LICENSE ├── README.md ├── case.go ├── case_test.go ├── delete.go ├── delete_ctx.go ├── delete_ctx_test.go ├── delete_test.go ├── expr.go ├── expr_test.go ├── go.mod ├── go.sum ├── insert.go ├── insert_ctx.go ├── insert_ctx_test.go ├── insert_test.go ├── integration/ │ ├── doc.go │ ├── go.mod │ ├── go.sum │ └── integration_test.go ├── part.go ├── placeholder.go ├── placeholder_test.go ├── row.go ├── row_test.go ├── select.go ├── select_ctx.go ├── select_ctx_test.go ├── select_test.go ├── squirrel.go ├── squirrel_ctx.go ├── squirrel_ctx_test.go ├── squirrel_test.go ├── statement.go ├── statement_test.go ├── stmtcacher.go ├── stmtcacher_ctx.go ├── stmtcacher_ctx_test.go ├── stmtcacher_noctx.go ├── stmtcacher_test.go ├── update.go ├── update_ctx.go ├── update_ctx_test.go ├── update_test.go ├── where.go └── where_test.go ================================================ FILE CONTENTS ================================================ ================================================ FILE: .gitignore ================================================ squirrel.test ================================================ FILE: .travis.yml ================================================ language: go go: - 1.11.x - 1.12.x - 1.13.x services: - mysql - postgresql # Setting sudo access to false will let Travis CI use containers rather than # VMs to run the tests. For more details see: # - http://docs.travis-ci.com/user/workers/container-based-infrastructure/ # - http://docs.travis-ci.com/user/workers/standard-infrastructure/ sudo: false before_script: - mysql -e 'CREATE DATABASE squirrel;' - psql -c 'CREATE DATABASE squirrel;' -U postgres script: - go test - cd integration - go test -args -driver sqlite3 - go test -args -driver mysql -dataSource travis@/squirrel - go test -args -driver postgres -dataSource 'postgres://postgres@localhost/squirrel?sslmode=disable' notifications: irc: "irc.freenode.net#masterminds" ================================================ FILE: LICENSE ================================================ MIT License Squirrel: The Masterminds Copyright (c) 2014-2015, Lann Martin. Copyright (C) 2015-2016, Google. Copyright (C) 2015, Matt Farina and Matt Butcher. Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal in the Software without restriction, including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so, subject to the following conditions: The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software. THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. ================================================ FILE: README.md ================================================ [![Stability: Maintenance](https://masterminds.github.io/stability/maintenance.svg)](https://masterminds.github.io/stability/maintenance.html) ### Squirrel is "complete". Bug fixes will still be merged (slowly). Bug reports are welcome, but I will not necessarily respond to them. If another fork (or substantially similar project) actively improves on what Squirrel does, let me know and I may link to it here. # Squirrel - fluent SQL generator for Go ```go import "github.com/Masterminds/squirrel" ``` [![GoDoc](https://godoc.org/github.com/Masterminds/squirrel?status.png)](https://godoc.org/github.com/Masterminds/squirrel) [![Build Status](https://api.travis-ci.org/Masterminds/squirrel.svg?branch=master)](https://travis-ci.org/Masterminds/squirrel) **Squirrel is not an ORM.** For an application of Squirrel, check out [structable, a table-struct mapper](https://github.com/Masterminds/structable) Squirrel helps you build SQL queries from composable parts: ```go import sq "github.com/Masterminds/squirrel" users := sq.Select("*").From("users").Join("emails USING (email_id)") active := users.Where(sq.Eq{"deleted_at": nil}) sql, args, err := active.ToSql() sql == "SELECT * FROM users JOIN emails USING (email_id) WHERE deleted_at IS NULL" ``` ```go sql, args, err := sq. Insert("users").Columns("name", "age"). Values("moe", 13).Values("larry", sq.Expr("? + 5", 12)). ToSql() sql == "INSERT INTO users (name,age) VALUES (?,?),(?,? + 5)" ``` Squirrel can also execute queries directly: ```go stooges := users.Where(sq.Eq{"username": []string{"moe", "larry", "curly", "shemp"}}) three_stooges := stooges.Limit(3) rows, err := three_stooges.RunWith(db).Query() // Behaves like: rows, err := db.Query("SELECT * FROM users WHERE username IN (?,?,?,?) LIMIT 3", "moe", "larry", "curly", "shemp") ``` Squirrel makes conditional query building a breeze: ```go if len(q) > 0 { users = users.Where("name LIKE ?", fmt.Sprint("%", q, "%")) } ``` Squirrel wants to make your life easier: ```go // StmtCache caches Prepared Stmts for you dbCache := sq.NewStmtCache(db) // StatementBuilder keeps your syntax neat mydb := sq.StatementBuilder.RunWith(dbCache) select_users := mydb.Select("*").From("users") ``` Squirrel loves PostgreSQL: ```go psql := sq.StatementBuilder.PlaceholderFormat(sq.Dollar) // You use question marks for placeholders... sql, _, _ := psql.Select("*").From("elephants").Where("name IN (?,?)", "Dumbo", "Verna").ToSql() /// ...squirrel replaces them using PlaceholderFormat. sql == "SELECT * FROM elephants WHERE name IN ($1,$2)" /// You can retrieve id ... query := sq.Insert("nodes"). Columns("uuid", "type", "data"). Values(node.Uuid, node.Type, node.Data). Suffix("RETURNING \"id\""). RunWith(m.db). PlaceholderFormat(sq.Dollar) query.QueryRow().Scan(&node.id) ``` You can escape question marks by inserting two question marks: ```sql SELECT * FROM nodes WHERE meta->'format' ??| array[?,?] ``` will generate with the Dollar Placeholder: ```sql SELECT * FROM nodes WHERE meta->'format' ?| array[$1,$2] ``` ## FAQ * **How can I build an IN query on composite keys / tuples, e.g. `WHERE (col1, col2) IN ((1,2),(3,4))`? ([#104](https://github.com/Masterminds/squirrel/issues/104))** Squirrel does not explicitly support tuples, but you can get the same effect with e.g.: ```go sq.Or{ sq.Eq{"col1": 1, "col2": 2}, sq.Eq{"col1": 3, "col2": 4}} ``` ```sql WHERE (col1 = 1 AND col2 = 2) OR (col1 = 3 AND col2 = 4) ``` (which should produce the same query plan as the tuple version) * **Why doesn't `Eq{"mynumber": []uint8{1,2,3}}` turn into an `IN` query? ([#114](https://github.com/Masterminds/squirrel/issues/114))** Values of type `[]byte` are handled specially by `database/sql`. In Go, [`byte` is just an alias of `uint8`](https://golang.org/pkg/builtin/#byte), so there is no way to distinguish `[]uint8` from `[]byte`. * **Some features are poorly documented!** This isn't a frequent complaints section! * **Some features are poorly documented?** Yes. The tests should be considered a part of the documentation; take a look at those for ideas on how to express more complex queries. ## License Squirrel is released under the [MIT License](http://www.opensource.org/licenses/MIT). ================================================ FILE: case.go ================================================ package squirrel import ( "bytes" "errors" "github.com/lann/builder" ) func init() { builder.Register(CaseBuilder{}, caseData{}) } // sqlizerBuffer is a helper that allows to write many Sqlizers one by one // without constant checks for errors that may come from Sqlizer type sqlizerBuffer struct { bytes.Buffer args []interface{} err error } // WriteSql converts Sqlizer to SQL strings and writes it to buffer func (b *sqlizerBuffer) WriteSql(item Sqlizer) { if b.err != nil { return } var str string var args []interface{} str, args, b.err = nestedToSql(item) if b.err != nil { return } b.WriteString(str) b.WriteByte(' ') b.args = append(b.args, args...) } func (b *sqlizerBuffer) ToSql() (string, []interface{}, error) { return b.String(), b.args, b.err } // whenPart is a helper structure to describe SQLs "WHEN ... THEN ..." expression type whenPart struct { when Sqlizer then Sqlizer } func newWhenPart(when interface{}, then interface{}) whenPart { return whenPart{newPart(when), newPart(then)} } // caseData holds all the data required to build a CASE SQL construct type caseData struct { What Sqlizer WhenParts []whenPart Else Sqlizer } // ToSql implements Sqlizer func (d *caseData) ToSql() (sqlStr string, args []interface{}, err error) { if len(d.WhenParts) == 0 { err = errors.New("case expression must contain at lease one WHEN clause") return } sql := sqlizerBuffer{} sql.WriteString("CASE ") if d.What != nil { sql.WriteSql(d.What) } for _, p := range d.WhenParts { sql.WriteString("WHEN ") sql.WriteSql(p.when) sql.WriteString("THEN ") sql.WriteSql(p.then) } if d.Else != nil { sql.WriteString("ELSE ") sql.WriteSql(d.Else) } sql.WriteString("END") return sql.ToSql() } // CaseBuilder builds SQL CASE construct which could be used as parts of queries. type CaseBuilder builder.Builder // ToSql builds the query into a SQL string and bound args. func (b CaseBuilder) ToSql() (string, []interface{}, error) { data := builder.GetStruct(b).(caseData) return data.ToSql() } // MustSql builds the query into a SQL string and bound args. // It panics if there are any errors. func (b CaseBuilder) MustSql() (string, []interface{}) { sql, args, err := b.ToSql() if err != nil { panic(err) } return sql, args } // what sets optional value for CASE construct "CASE [value] ..." func (b CaseBuilder) what(expr interface{}) CaseBuilder { return builder.Set(b, "What", newPart(expr)).(CaseBuilder) } // When adds "WHEN ... THEN ..." part to CASE construct func (b CaseBuilder) When(when interface{}, then interface{}) CaseBuilder { // TODO: performance hint: replace slice of WhenPart with just slice of parts // where even indices of the slice belong to "when"s and odd indices belong to "then"s return builder.Append(b, "WhenParts", newWhenPart(when, then)).(CaseBuilder) } // What sets optional "ELSE ..." part for CASE construct func (b CaseBuilder) Else(expr interface{}) CaseBuilder { return builder.Set(b, "Else", newPart(expr)).(CaseBuilder) } ================================================ FILE: case_test.go ================================================ package squirrel import ( "testing" "github.com/stretchr/testify/assert" ) func TestCaseWithVal(t *testing.T) { caseStmt := Case("number"). When("1", "one"). When("2", "two"). Else(Expr("?", "big number")) qb := Select(). Column(caseStmt). From("table") sql, args, err := qb.ToSql() assert.NoError(t, err) expectedSql := "SELECT CASE number " + "WHEN 1 THEN one " + "WHEN 2 THEN two " + "ELSE ? " + "END " + "FROM table" assert.Equal(t, expectedSql, sql) expectedArgs := []interface{}{"big number"} assert.Equal(t, expectedArgs, args) } func TestCaseWithComplexVal(t *testing.T) { caseStmt := Case("? > ?", 10, 5). When("true", "'T'") qb := Select(). Column(Alias(caseStmt, "complexCase")). From("table") sql, args, err := qb.ToSql() assert.NoError(t, err) expectedSql := "SELECT (CASE ? > ? " + "WHEN true THEN 'T' " + "END) AS complexCase " + "FROM table" assert.Equal(t, expectedSql, sql) expectedArgs := []interface{}{10, 5} assert.Equal(t, expectedArgs, args) } func TestCaseWithNoVal(t *testing.T) { caseStmt := Case(). When(Eq{"x": 0}, "x is zero"). When(Expr("x > ?", 1), Expr("CONCAT('x is greater than ', ?)", 2)) qb := Select().Column(caseStmt).From("table") sql, args, err := qb.ToSql() assert.NoError(t, err) expectedSql := "SELECT CASE " + "WHEN x = ? THEN x is zero " + "WHEN x > ? THEN CONCAT('x is greater than ', ?) " + "END " + "FROM table" assert.Equal(t, expectedSql, sql) expectedArgs := []interface{}{0, 1, 2} assert.Equal(t, expectedArgs, args) } func TestCaseWithExpr(t *testing.T) { caseStmt := Case(Expr("x = ?", true)). When("true", Expr("?", "it's true!")). Else("42") qb := Select().Column(caseStmt).From("table") sql, args, err := qb.ToSql() assert.NoError(t, err) expectedSql := "SELECT CASE x = ? " + "WHEN true THEN ? " + "ELSE 42 " + "END " + "FROM table" assert.Equal(t, expectedSql, sql) expectedArgs := []interface{}{true, "it's true!"} assert.Equal(t, expectedArgs, args) } func TestMultipleCase(t *testing.T) { caseStmtNoval := Case(Expr("x = ?", true)). When("true", Expr("?", "it's true!")). Else("42") caseStmtExpr := Case(). When(Eq{"x": 0}, "'x is zero'"). When(Expr("x > ?", 1), Expr("CONCAT('x is greater than ', ?)", 2)) qb := Select(). Column(Alias(caseStmtNoval, "case_noval")). Column(Alias(caseStmtExpr, "case_expr")). From("table") sql, args, err := qb.ToSql() assert.NoError(t, err) expectedSql := "SELECT " + "(CASE x = ? WHEN true THEN ? ELSE 42 END) AS case_noval, " + "(CASE WHEN x = ? THEN 'x is zero' WHEN x > ? THEN CONCAT('x is greater than ', ?) END) AS case_expr " + "FROM table" assert.Equal(t, expectedSql, sql) expectedArgs := []interface{}{ true, "it's true!", 0, 1, 2, } assert.Equal(t, expectedArgs, args) } func TestCaseWithNoWhenClause(t *testing.T) { caseStmt := Case("something"). Else("42") qb := Select().Column(caseStmt).From("table") _, _, err := qb.ToSql() assert.Error(t, err) assert.Equal(t, "case expression must contain at lease one WHEN clause", err.Error()) } func TestCaseBuilderMustSql(t *testing.T) { defer func() { if r := recover(); r == nil { t.Errorf("TestCaseBuilderMustSql should have panicked!") } }() Case("").MustSql() } ================================================ FILE: delete.go ================================================ package squirrel import ( "bytes" "database/sql" "fmt" "strings" "github.com/lann/builder" ) type deleteData struct { PlaceholderFormat PlaceholderFormat RunWith BaseRunner Prefixes []Sqlizer From string WhereParts []Sqlizer OrderBys []string Limit string Offset string Suffixes []Sqlizer } func (d *deleteData) Exec() (sql.Result, error) { if d.RunWith == nil { return nil, RunnerNotSet } return ExecWith(d.RunWith, d) } func (d *deleteData) ToSql() (sqlStr string, args []interface{}, err error) { if len(d.From) == 0 { err = fmt.Errorf("delete statements must specify a From table") return } sql := &bytes.Buffer{} if len(d.Prefixes) > 0 { args, err = appendToSql(d.Prefixes, sql, " ", args) if err != nil { return } sql.WriteString(" ") } sql.WriteString("DELETE FROM ") sql.WriteString(d.From) if len(d.WhereParts) > 0 { sql.WriteString(" WHERE ") args, err = appendToSql(d.WhereParts, sql, " AND ", args) if err != nil { return } } if len(d.OrderBys) > 0 { sql.WriteString(" ORDER BY ") sql.WriteString(strings.Join(d.OrderBys, ", ")) } 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, err = d.PlaceholderFormat.ReplacePlaceholders(sql.String()) return } // Builder // DeleteBuilder builds SQL DELETE statements. type DeleteBuilder builder.Builder func init() { builder.Register(DeleteBuilder{}, deleteData{}) } // Format methods // PlaceholderFormat sets PlaceholderFormat (e.g. Question or Dollar) for the // query. func (b DeleteBuilder) PlaceholderFormat(f PlaceholderFormat) DeleteBuilder { return builder.Set(b, "PlaceholderFormat", f).(DeleteBuilder) } // Runner methods // RunWith sets a Runner (like database/sql.DB) to be used with e.g. Exec. func (b DeleteBuilder) RunWith(runner BaseRunner) DeleteBuilder { return setRunWith(b, runner).(DeleteBuilder) } // Exec builds and Execs the query with the Runner set by RunWith. func (b DeleteBuilder) Exec() (sql.Result, error) { data := builder.GetStruct(b).(deleteData) return data.Exec() } // SQL methods // ToSql builds the query into a SQL string and bound args. func (b DeleteBuilder) ToSql() (string, []interface{}, error) { data := builder.GetStruct(b).(deleteData) return data.ToSql() } // MustSql builds the query into a SQL string and bound args. // It panics if there are any errors. func (b DeleteBuilder) MustSql() (string, []interface{}) { sql, args, err := b.ToSql() if err != nil { panic(err) } return sql, args } // Prefix adds an expression to the beginning of the query func (b DeleteBuilder) Prefix(sql string, args ...interface{}) DeleteBuilder { return b.PrefixExpr(Expr(sql, args...)) } // PrefixExpr adds an expression to the very beginning of the query func (b DeleteBuilder) PrefixExpr(expr Sqlizer) DeleteBuilder { return builder.Append(b, "Prefixes", expr).(DeleteBuilder) } // From sets the table to be deleted from. func (b DeleteBuilder) From(from string) DeleteBuilder { return builder.Set(b, "From", from).(DeleteBuilder) } // Where adds WHERE expressions to the query. // // See SelectBuilder.Where for more information. func (b DeleteBuilder) Where(pred interface{}, args ...interface{}) DeleteBuilder { return builder.Append(b, "WhereParts", newWherePart(pred, args...)).(DeleteBuilder) } // OrderBy adds ORDER BY expressions to the query. func (b DeleteBuilder) OrderBy(orderBys ...string) DeleteBuilder { return builder.Extend(b, "OrderBys", orderBys).(DeleteBuilder) } // Limit sets a LIMIT clause on the query. func (b DeleteBuilder) Limit(limit uint64) DeleteBuilder { return builder.Set(b, "Limit", fmt.Sprintf("%d", limit)).(DeleteBuilder) } // Offset sets a OFFSET clause on the query. func (b DeleteBuilder) Offset(offset uint64) DeleteBuilder { return builder.Set(b, "Offset", fmt.Sprintf("%d", offset)).(DeleteBuilder) } // Suffix adds an expression to the end of the query func (b DeleteBuilder) Suffix(sql string, args ...interface{}) DeleteBuilder { return b.SuffixExpr(Expr(sql, args...)) } // SuffixExpr adds an expression to the end of the query func (b DeleteBuilder) SuffixExpr(expr Sqlizer) DeleteBuilder { return builder.Append(b, "Suffixes", expr).(DeleteBuilder) } func (b DeleteBuilder) Query() (*sql.Rows, error) { data := builder.GetStruct(b).(deleteData) return data.Query() } func (d *deleteData) Query() (*sql.Rows, error) { if d.RunWith == nil { return nil, RunnerNotSet } return QueryWith(d.RunWith, d) } ================================================ FILE: delete_ctx.go ================================================ // +build go1.8 package squirrel import ( "context" "database/sql" "github.com/lann/builder" ) func (d *deleteData) ExecContext(ctx context.Context) (sql.Result, error) { if d.RunWith == nil { return nil, RunnerNotSet } ctxRunner, ok := d.RunWith.(ExecerContext) if !ok { return nil, NoContextSupport } return ExecContextWith(ctx, ctxRunner, d) } func (d *deleteData) QueryContext(ctx context.Context) (*sql.Rows, error) { if d.RunWith == nil { return nil, RunnerNotSet } ctxRunner, ok := d.RunWith.(QueryerContext) if !ok { return nil, NoContextSupport } return QueryContextWith(ctx, ctxRunner, d) } func (d *deleteData) QueryRowContext(ctx context.Context) RowScanner { if d.RunWith == nil { return &Row{err: RunnerNotSet} } queryRower, ok := d.RunWith.(QueryRowerContext) if !ok { if _, ok := d.RunWith.(QueryerContext); !ok { return &Row{err: RunnerNotQueryRunner} } return &Row{err: NoContextSupport} } return QueryRowContextWith(ctx, queryRower, d) } // ExecContext builds and ExecContexts the query with the Runner set by RunWith. func (b DeleteBuilder) ExecContext(ctx context.Context) (sql.Result, error) { data := builder.GetStruct(b).(deleteData) return data.ExecContext(ctx) } // QueryContext builds and QueryContexts the query with the Runner set by RunWith. func (b DeleteBuilder) QueryContext(ctx context.Context) (*sql.Rows, error) { data := builder.GetStruct(b).(deleteData) return data.QueryContext(ctx) } // QueryRowContext builds and QueryRowContexts the query with the Runner set by RunWith. func (b DeleteBuilder) QueryRowContext(ctx context.Context) RowScanner { data := builder.GetStruct(b).(deleteData) return data.QueryRowContext(ctx) } // ScanContext is a shortcut for QueryRowContext().Scan. func (b DeleteBuilder) ScanContext(ctx context.Context, dest ...interface{}) error { return b.QueryRowContext(ctx).Scan(dest...) } ================================================ FILE: delete_ctx_test.go ================================================ // +build go1.8 package squirrel import ( "testing" "github.com/stretchr/testify/assert" ) func TestDeleteBuilderContextRunners(t *testing.T) { db := &DBStub{} b := Delete("test").Where("x = ?", 1).RunWith(db) expectedSql := "DELETE FROM test WHERE x = ?" b.ExecContext(ctx) assert.Equal(t, expectedSql, db.LastExecSql) b.QueryContext(ctx) assert.Equal(t, expectedSql, db.LastQuerySql) b.QueryRowContext(ctx) assert.Equal(t, expectedSql, db.LastQueryRowSql) err := b.ScanContext(ctx) assert.NoError(t, err) } func TestDeleteBuilderContextNoRunner(t *testing.T) { b := Delete("test").Where("x != ?", 0).Suffix("RETURNING x") _, err := b.ExecContext(ctx) assert.Equal(t, RunnerNotSet, err) _, err = b.QueryContext(ctx) assert.Equal(t, RunnerNotSet, err) err = b.ScanContext(ctx) assert.Equal(t, RunnerNotSet, err) } ================================================ FILE: delete_test.go ================================================ package squirrel import ( "testing" "github.com/stretchr/testify/assert" ) func TestDeleteBuilderToSql(t *testing.T) { b := Delete(""). Prefix("WITH prefix AS ?", 0). From("a"). Where("b = ?", 1). OrderBy("c"). Limit(2). Offset(3). Suffix("RETURNING ?", 4) sql, args, err := b.ToSql() assert.NoError(t, err) expectedSql := "WITH prefix AS ? " + "DELETE FROM a WHERE b = ? ORDER BY c LIMIT 2 OFFSET 3 " + "RETURNING ?" assert.Equal(t, expectedSql, sql) expectedArgs := []interface{}{0, 1, 4} assert.Equal(t, expectedArgs, args) } func TestDeleteBuilderToSqlErr(t *testing.T) { _, _, err := Delete("").ToSql() assert.Error(t, err) } func TestDeleteBuilderMustSql(t *testing.T) { defer func() { if r := recover(); r == nil { t.Errorf("TestDeleteBuilderMustSql should have panicked!") } }() Delete("").MustSql() } func TestDeleteBuilderPlaceholders(t *testing.T) { b := Delete("test").Where("x = ? AND y = ?", 1, 2) sql, _, _ := b.PlaceholderFormat(Question).ToSql() assert.Equal(t, "DELETE FROM test WHERE x = ? AND y = ?", sql) sql, _, _ = b.PlaceholderFormat(Dollar).ToSql() assert.Equal(t, "DELETE FROM test WHERE x = $1 AND y = $2", sql) } func TestDeleteBuilderRunners(t *testing.T) { db := &DBStub{} b := Delete("test").Where("x = ?", 1).RunWith(db) expectedSql := "DELETE FROM test WHERE x = ?" b.Exec() assert.Equal(t, expectedSql, db.LastExecSql) } func TestDeleteBuilderNoRunner(t *testing.T) { b := Delete("test") _, err := b.Exec() assert.Equal(t, RunnerNotSet, err) } func TestDeleteWithQuery(t *testing.T) { db := &DBStub{} b := Delete("test").Where("id=55").Suffix("RETURNING path").RunWith(db) expectedSql := "DELETE FROM test WHERE id=55 RETURNING path" b.Query() assert.Equal(t, expectedSql, db.LastQuerySql) } ================================================ FILE: expr.go ================================================ package squirrel import ( "bytes" "database/sql/driver" "fmt" "reflect" "sort" "strings" ) const ( // Portable true/false literals. sqlTrue = "(1=1)" sqlFalse = "(1=0)" ) type expr struct { sql string args []interface{} } // Expr builds an expression from a SQL fragment and arguments. // // Ex: // Expr("FROM_UNIXTIME(?)", t) func Expr(sql string, args ...interface{}) Sqlizer { return expr{sql: sql, args: args} } func (e expr) ToSql() (sql string, args []interface{}, err error) { simple := true for _, arg := range e.args { if _, ok := arg.(Sqlizer); ok { simple = false } } if simple { return e.sql, e.args, nil } buf := &bytes.Buffer{} ap := e.args sp := e.sql var isql string var iargs []interface{} for err == nil && len(ap) > 0 && len(sp) > 0 { i := strings.Index(sp, "?") if i < 0 { // no more placeholders break } if len(sp) > i+1 && sp[i+1:i+2] == "?" { // escaped "??"; append it and step past buf.WriteString(sp[:i+2]) sp = sp[i+2:] continue } if as, ok := ap[0].(Sqlizer); ok { // sqlizer argument; expand it and append the result isql, iargs, err = as.ToSql() buf.WriteString(sp[:i]) buf.WriteString(isql) args = append(args, iargs...) } else { // normal argument; append it and the placeholder buf.WriteString(sp[:i+1]) args = append(args, ap[0]) } // step past the argument and placeholder ap = ap[1:] sp = sp[i+1:] } // append the remaining sql and arguments buf.WriteString(sp) return buf.String(), append(args, ap...), err } type concatExpr []interface{} func (ce concatExpr) ToSql() (sql string, args []interface{}, err error) { for _, part := range ce { switch p := part.(type) { case string: sql += p case Sqlizer: pSql, pArgs, err := p.ToSql() if err != nil { return "", nil, err } sql += pSql args = append(args, pArgs...) default: return "", nil, fmt.Errorf("%#v is not a string or Sqlizer", part) } } return } // ConcatExpr builds an expression by concatenating strings and other expressions. // // Ex: // name_expr := Expr("CONCAT(?, ' ', ?)", firstName, lastName) // ConcatExpr("COALESCE(full_name,", name_expr, ")") func ConcatExpr(parts ...interface{}) concatExpr { return concatExpr(parts) } // aliasExpr helps to alias part of SQL query generated with underlying "expr" type aliasExpr struct { expr Sqlizer alias string } // Alias allows to define alias for column in SelectBuilder. Useful when column is // defined as complex expression like IF or CASE // Ex: // .Column(Alias(caseStmt, "case_column")) func Alias(expr Sqlizer, alias string) aliasExpr { return aliasExpr{expr, alias} } func (e aliasExpr) ToSql() (sql string, args []interface{}, err error) { sql, args, err = e.expr.ToSql() if err == nil { sql = fmt.Sprintf("(%s) AS %s", sql, e.alias) } return } // Eq is syntactic sugar for use with Where/Having/Set methods. type Eq map[string]interface{} func (eq Eq) toSQL(useNotOpr bool) (sql string, args []interface{}, err error) { if len(eq) == 0 { // Empty Sql{} evaluates to true. sql = sqlTrue return } var ( exprs []string equalOpr = "=" inOpr = "IN" nullOpr = "IS" inEmptyExpr = sqlFalse ) if useNotOpr { equalOpr = "<>" inOpr = "NOT IN" nullOpr = "IS NOT" inEmptyExpr = sqlTrue } sortedKeys := getSortedKeys(eq) for _, key := range sortedKeys { var expr string val := eq[key] switch v := val.(type) { case driver.Valuer: if val, err = v.Value(); err != nil { return } } r := reflect.ValueOf(val) if r.Kind() == reflect.Ptr { if r.IsNil() { val = nil } else { val = r.Elem().Interface() } } if val == nil { expr = fmt.Sprintf("%s %s NULL", key, nullOpr) } else { if isListType(val) { valVal := reflect.ValueOf(val) if valVal.Len() == 0 { expr = inEmptyExpr if args == nil { args = []interface{}{} } } else { for i := 0; i < valVal.Len(); i++ { args = append(args, valVal.Index(i).Interface()) } expr = fmt.Sprintf("%s %s (%s)", key, inOpr, Placeholders(valVal.Len())) } } else { expr = fmt.Sprintf("%s %s ?", key, equalOpr) args = append(args, val) } } exprs = append(exprs, expr) } sql = strings.Join(exprs, " AND ") return } func (eq Eq) ToSql() (sql string, args []interface{}, err error) { return eq.toSQL(false) } // NotEq is syntactic sugar for use with Where/Having/Set methods. // Ex: // .Where(NotEq{"id": 1}) == "id <> 1" type NotEq Eq func (neq NotEq) ToSql() (sql string, args []interface{}, err error) { return Eq(neq).toSQL(true) } // Like is syntactic sugar for use with LIKE conditions. // Ex: // .Where(Like{"name": "%irrel"}) type Like map[string]interface{} func (lk Like) toSql(opr string) (sql string, args []interface{}, err error) { var exprs []string for key, val := range lk { expr := "" switch v := val.(type) { case driver.Valuer: if val, err = v.Value(); err != nil { return } } if val == nil { err = fmt.Errorf("cannot use null with like operators") return } else { if isListType(val) { err = fmt.Errorf("cannot use array or slice with like operators") return } else { expr = fmt.Sprintf("%s %s ?", key, opr) args = append(args, val) } } exprs = append(exprs, expr) } sql = strings.Join(exprs, " AND ") return } func (lk Like) ToSql() (sql string, args []interface{}, err error) { return lk.toSql("LIKE") } // NotLike is syntactic sugar for use with LIKE conditions. // Ex: // .Where(NotLike{"name": "%irrel"}) type NotLike Like func (nlk NotLike) ToSql() (sql string, args []interface{}, err error) { return Like(nlk).toSql("NOT LIKE") } // ILike is syntactic sugar for use with ILIKE conditions. // Ex: // .Where(ILike{"name": "sq%"}) type ILike Like func (ilk ILike) ToSql() (sql string, args []interface{}, err error) { return Like(ilk).toSql("ILIKE") } // NotILike is syntactic sugar for use with ILIKE conditions. // Ex: // .Where(NotILike{"name": "sq%"}) type NotILike Like func (nilk NotILike) ToSql() (sql string, args []interface{}, err error) { return Like(nilk).toSql("NOT ILIKE") } // Lt is syntactic sugar for use with Where/Having/Set methods. // Ex: // .Where(Lt{"id": 1}) type Lt map[string]interface{} func (lt Lt) toSql(opposite, orEq bool) (sql string, args []interface{}, err error) { var ( exprs []string opr = "<" ) if opposite { opr = ">" } if orEq { opr = fmt.Sprintf("%s%s", opr, "=") } sortedKeys := getSortedKeys(lt) for _, key := range sortedKeys { var expr string val := lt[key] switch v := val.(type) { case driver.Valuer: if val, err = v.Value(); err != nil { return } } if val == nil { err = fmt.Errorf("cannot use null with less than or greater than operators") return } if isListType(val) { err = fmt.Errorf("cannot use array or slice with less than or greater than operators") return } expr = fmt.Sprintf("%s %s ?", key, opr) args = append(args, val) exprs = append(exprs, expr) } sql = strings.Join(exprs, " AND ") return } func (lt Lt) ToSql() (sql string, args []interface{}, err error) { return lt.toSql(false, false) } // LtOrEq is syntactic sugar for use with Where/Having/Set methods. // Ex: // .Where(LtOrEq{"id": 1}) == "id <= 1" type LtOrEq Lt func (ltOrEq LtOrEq) ToSql() (sql string, args []interface{}, err error) { return Lt(ltOrEq).toSql(false, true) } // Gt is syntactic sugar for use with Where/Having/Set methods. // Ex: // .Where(Gt{"id": 1}) == "id > 1" type Gt Lt func (gt Gt) ToSql() (sql string, args []interface{}, err error) { return Lt(gt).toSql(true, false) } // GtOrEq is syntactic sugar for use with Where/Having/Set methods. // Ex: // .Where(GtOrEq{"id": 1}) == "id >= 1" type GtOrEq Lt func (gtOrEq GtOrEq) ToSql() (sql string, args []interface{}, err error) { return Lt(gtOrEq).toSql(true, true) } type conj []Sqlizer func (c conj) join(sep, defaultExpr string) (sql string, args []interface{}, err error) { if len(c) == 0 { return defaultExpr, []interface{}{}, nil } var sqlParts []string for _, sqlizer := range c { partSQL, partArgs, err := nestedToSql(sqlizer) if err != nil { return "", nil, err } if partSQL != "" { sqlParts = append(sqlParts, partSQL) args = append(args, partArgs...) } } if len(sqlParts) > 0 { sql = fmt.Sprintf("(%s)", strings.Join(sqlParts, sep)) } return } // And conjunction Sqlizers type And conj func (a And) ToSql() (string, []interface{}, error) { return conj(a).join(" AND ", sqlTrue) } // Or conjunction Sqlizers type Or conj func (o Or) ToSql() (string, []interface{}, error) { return conj(o).join(" OR ", sqlFalse) } func getSortedKeys(exp map[string]interface{}) []string { sortedKeys := make([]string, 0, len(exp)) for k := range exp { sortedKeys = append(sortedKeys, k) } sort.Strings(sortedKeys) return sortedKeys } func isListType(val interface{}) bool { if driver.IsValue(val) { return false } valVal := reflect.ValueOf(val) return valVal.Kind() == reflect.Array || valVal.Kind() == reflect.Slice } ================================================ FILE: expr_test.go ================================================ package squirrel import ( "database/sql" "testing" "github.com/stretchr/testify/assert" ) func TestConcatExpr(t *testing.T) { b := ConcatExpr("COALESCE(name,", Expr("CONCAT(?,' ',?)", "f", "l"), ")") sql, args, err := b.ToSql() assert.NoError(t, err) expectedSql := "COALESCE(name,CONCAT(?,' ',?))" assert.Equal(t, expectedSql, sql) expectedArgs := []interface{}{"f", "l"} assert.Equal(t, expectedArgs, args) } func TestConcatExprBadType(t *testing.T) { b := ConcatExpr("prefix", 123, "suffix") _, _, err := b.ToSql() assert.Error(t, err) assert.Contains(t, err.Error(), "123 is not") } func TestEqToSql(t *testing.T) { b := Eq{"id": 1} sql, args, err := b.ToSql() assert.NoError(t, err) expectedSql := "id = ?" assert.Equal(t, expectedSql, sql) expectedArgs := []interface{}{1} assert.Equal(t, expectedArgs, args) } func TestEqEmptyToSql(t *testing.T) { sql, args, err := Eq{}.ToSql() assert.NoError(t, err) expectedSql := "(1=1)" assert.Equal(t, expectedSql, sql) assert.Empty(t, args) } func TestEqInToSql(t *testing.T) { b := Eq{"id": []int{1, 2, 3}} sql, args, err := b.ToSql() assert.NoError(t, err) expectedSql := "id IN (?,?,?)" assert.Equal(t, expectedSql, sql) expectedArgs := []interface{}{1, 2, 3} assert.Equal(t, expectedArgs, args) } func TestNotEqToSql(t *testing.T) { b := NotEq{"id": 1} sql, args, err := b.ToSql() assert.NoError(t, err) expectedSql := "id <> ?" assert.Equal(t, expectedSql, sql) expectedArgs := []interface{}{1} assert.Equal(t, expectedArgs, args) } func TestEqNotInToSql(t *testing.T) { b := NotEq{"id": []int{1, 2, 3}} sql, args, err := b.ToSql() assert.NoError(t, err) expectedSql := "id NOT IN (?,?,?)" assert.Equal(t, expectedSql, sql) expectedArgs := []interface{}{1, 2, 3} assert.Equal(t, expectedArgs, args) } func TestEqInEmptyToSql(t *testing.T) { b := Eq{"id": []int{}} sql, args, err := b.ToSql() assert.NoError(t, err) expectedSql := "(1=0)" assert.Equal(t, expectedSql, sql) expectedArgs := []interface{}{} assert.Equal(t, expectedArgs, args) } func TestNotEqInEmptyToSql(t *testing.T) { b := NotEq{"id": []int{}} sql, args, err := b.ToSql() assert.NoError(t, err) expectedSql := "(1=1)" assert.Equal(t, expectedSql, sql) expectedArgs := []interface{}{} assert.Equal(t, expectedArgs, args) } func TestEqBytesToSql(t *testing.T) { b := Eq{"id": []byte("test")} sql, args, err := b.ToSql() assert.NoError(t, err) expectedSql := "id = ?" assert.Equal(t, expectedSql, sql) expectedArgs := []interface{}{[]byte("test")} assert.Equal(t, expectedArgs, args) } func TestLtToSql(t *testing.T) { b := Lt{"id": 1} sql, args, err := b.ToSql() assert.NoError(t, err) expectedSql := "id < ?" assert.Equal(t, expectedSql, sql) expectedArgs := []interface{}{1} assert.Equal(t, expectedArgs, args) } func TestLtOrEqToSql(t *testing.T) { b := LtOrEq{"id": 1} sql, args, err := b.ToSql() assert.NoError(t, err) expectedSql := "id <= ?" assert.Equal(t, expectedSql, sql) expectedArgs := []interface{}{1} assert.Equal(t, expectedArgs, args) } func TestGtToSql(t *testing.T) { b := Gt{"id": 1} sql, args, err := b.ToSql() assert.NoError(t, err) expectedSql := "id > ?" assert.Equal(t, expectedSql, sql) expectedArgs := []interface{}{1} assert.Equal(t, expectedArgs, args) } func TestGtOrEqToSql(t *testing.T) { b := GtOrEq{"id": 1} sql, args, err := b.ToSql() assert.NoError(t, err) expectedSql := "id >= ?" assert.Equal(t, expectedSql, sql) expectedArgs := []interface{}{1} assert.Equal(t, expectedArgs, args) } func TestExprNilToSql(t *testing.T) { var b Sqlizer b = NotEq{"name": nil} sql, args, err := b.ToSql() assert.NoError(t, err) assert.Empty(t, args) expectedSql := "name IS NOT NULL" assert.Equal(t, expectedSql, sql) b = Eq{"name": nil} sql, args, err = b.ToSql() assert.NoError(t, err) assert.Empty(t, args) expectedSql = "name IS NULL" assert.Equal(t, expectedSql, sql) } func TestNullTypeString(t *testing.T) { var b Sqlizer var name sql.NullString b = Eq{"name": name} sql, args, err := b.ToSql() assert.NoError(t, err) assert.Empty(t, args) assert.Equal(t, "name IS NULL", sql) name.Scan("Name") b = Eq{"name": name} sql, args, err = b.ToSql() assert.NoError(t, err) assert.Equal(t, []interface{}{"Name"}, args) assert.Equal(t, "name = ?", sql) } func TestNullTypeInt64(t *testing.T) { var userID sql.NullInt64 userID.Scan(nil) b := Eq{"user_id": userID} sql, args, err := b.ToSql() assert.NoError(t, err) assert.Empty(t, args) assert.Equal(t, "user_id IS NULL", sql) userID.Scan(int64(10)) b = Eq{"user_id": userID} sql, args, err = b.ToSql() assert.NoError(t, err) assert.Equal(t, []interface{}{int64(10)}, args) assert.Equal(t, "user_id = ?", sql) } func TestNilPointer(t *testing.T) { var name *string = nil eq := Eq{"name": name} sql, args, err := eq.ToSql() assert.NoError(t, err) assert.Empty(t, args) assert.Equal(t, "name IS NULL", sql) neq := NotEq{"name": name} sql, args, err = neq.ToSql() assert.NoError(t, err) assert.Empty(t, args) assert.Equal(t, "name IS NOT NULL", sql) var ids *[]int = nil eq = Eq{"id": ids} sql, args, err = eq.ToSql() assert.NoError(t, err) assert.Empty(t, args) assert.Equal(t, "id IS NULL", sql) neq = NotEq{"id": ids} sql, args, err = neq.ToSql() assert.NoError(t, err) assert.Empty(t, args) assert.Equal(t, "id IS NOT NULL", sql) var ida *[3]int = nil eq = Eq{"id": ida} sql, args, err = eq.ToSql() assert.NoError(t, err) assert.Empty(t, args) assert.Equal(t, "id IS NULL", sql) neq = NotEq{"id": ida} sql, args, err = neq.ToSql() assert.NoError(t, err) assert.Empty(t, args) assert.Equal(t, "id IS NOT NULL", sql) } func TestNotNilPointer(t *testing.T) { c := "Name" name := &c eq := Eq{"name": name} sql, args, err := eq.ToSql() assert.NoError(t, err) assert.Equal(t, []interface{}{"Name"}, args) assert.Equal(t, "name = ?", sql) neq := NotEq{"name": name} sql, args, err = neq.ToSql() assert.NoError(t, err) assert.Equal(t, []interface{}{"Name"}, args) assert.Equal(t, "name <> ?", sql) s := []int{1, 2, 3} ids := &s eq = Eq{"id": ids} sql, args, err = eq.ToSql() assert.NoError(t, err) assert.Equal(t, []interface{}{1, 2, 3}, args) assert.Equal(t, "id IN (?,?,?)", sql) neq = NotEq{"id": ids} sql, args, err = neq.ToSql() assert.NoError(t, err) assert.Equal(t, []interface{}{1, 2, 3}, args) assert.Equal(t, "id NOT IN (?,?,?)", sql) a := [3]int{1, 2, 3} ida := &a eq = Eq{"id": ida} sql, args, err = eq.ToSql() assert.NoError(t, err) assert.Equal(t, []interface{}{1, 2, 3}, args) assert.Equal(t, "id IN (?,?,?)", sql) neq = NotEq{"id": ida} sql, args, err = neq.ToSql() assert.NoError(t, err) assert.Equal(t, []interface{}{1, 2, 3}, args) assert.Equal(t, "id NOT IN (?,?,?)", sql) } func TestEmptyAndToSql(t *testing.T) { sql, args, err := And{}.ToSql() assert.NoError(t, err) expectedSql := "(1=1)" assert.Equal(t, expectedSql, sql) expectedArgs := []interface{}{} assert.Equal(t, expectedArgs, args) } func TestEmptyOrToSql(t *testing.T) { sql, args, err := Or{}.ToSql() assert.NoError(t, err) expectedSql := "(1=0)" assert.Equal(t, expectedSql, sql) expectedArgs := []interface{}{} assert.Equal(t, expectedArgs, args) } func TestLikeToSql(t *testing.T) { b := Like{"name": "%irrel"} sql, args, err := b.ToSql() assert.NoError(t, err) expectedSql := "name LIKE ?" assert.Equal(t, expectedSql, sql) expectedArgs := []interface{}{"%irrel"} assert.Equal(t, expectedArgs, args) } func TestNotLikeToSql(t *testing.T) { b := NotLike{"name": "%irrel"} sql, args, err := b.ToSql() assert.NoError(t, err) expectedSql := "name NOT LIKE ?" assert.Equal(t, expectedSql, sql) expectedArgs := []interface{}{"%irrel"} assert.Equal(t, expectedArgs, args) } func TestILikeToSql(t *testing.T) { b := ILike{"name": "sq%"} sql, args, err := b.ToSql() assert.NoError(t, err) expectedSql := "name ILIKE ?" assert.Equal(t, expectedSql, sql) expectedArgs := []interface{}{"sq%"} assert.Equal(t, expectedArgs, args) } func TestNotILikeToSql(t *testing.T) { b := NotILike{"name": "sq%"} sql, args, err := b.ToSql() assert.NoError(t, err) expectedSql := "name NOT ILIKE ?" assert.Equal(t, expectedSql, sql) expectedArgs := []interface{}{"sq%"} assert.Equal(t, expectedArgs, args) } func TestSqlEqOrder(t *testing.T) { b := Eq{"a": 1, "b": 2, "c": 3} sql, args, err := b.ToSql() assert.NoError(t, err) expectedSql := "a = ? AND b = ? AND c = ?" assert.Equal(t, expectedSql, sql) expectedArgs := []interface{}{1, 2, 3} assert.Equal(t, expectedArgs, args) } func TestSqlLtOrder(t *testing.T) { b := Lt{"a": 1, "b": 2, "c": 3} sql, args, err := b.ToSql() assert.NoError(t, err) expectedSql := "a < ? AND b < ? AND c < ?" assert.Equal(t, expectedSql, sql) expectedArgs := []interface{}{1, 2, 3} assert.Equal(t, expectedArgs, args) } func TestExprEscaped(t *testing.T) { b := Expr("count(??)", Expr("x")) sql, args, err := b.ToSql() assert.NoError(t, err) expectedSql := "count(??)" assert.Equal(t, expectedSql, sql) expectedArgs := []interface{}{Expr("x")} assert.Equal(t, expectedArgs, args) } func TestExprRecursion(t *testing.T) { { b := Expr("count(?)", Expr("nullif(a,?)", "b")) sql, args, err := b.ToSql() assert.NoError(t, err) expectedSql := "count(nullif(a,?))" assert.Equal(t, expectedSql, sql) expectedArgs := []interface{}{"b"} assert.Equal(t, expectedArgs, args) } { b := Expr("extract(? from ?)", Expr("epoch"), "2001-02-03") sql, args, err := b.ToSql() assert.NoError(t, err) expectedSql := "extract(epoch from ?)" assert.Equal(t, expectedSql, sql) expectedArgs := []interface{}{"2001-02-03"} assert.Equal(t, expectedArgs, args) } { b := Expr("JOIN t1 ON ?", And{Eq{"id": 1}, Expr("NOT c1"), Expr("? @@ ?", "x", "y")}) sql, args, err := b.ToSql() assert.NoError(t, err) expectedSql := "JOIN t1 ON (id = ? AND NOT c1 AND ? @@ ?)" assert.Equal(t, expectedSql, sql) expectedArgs := []interface{}{1, "x", "y"} assert.Equal(t, expectedArgs, args) } } func ExampleEq() { Select("id", "created", "first_name").From("users").Where(Eq{ "company": 20, }) } ================================================ FILE: go.mod ================================================ module github.com/Masterminds/squirrel go 1.14 require ( github.com/davecgh/go-spew v1.1.1 // indirect github.com/lann/builder v0.0.0-20180802200727-47ae307949d0 github.com/lann/ps v0.0.0-20150810152359-62de8c46ede0 // indirect github.com/pmezard/go-difflib v1.0.0 // indirect github.com/stretchr/testify v1.2.2 ) ================================================ FILE: go.sum ================================================ github.com/davecgh/go-spew v1.1.1 h1:vj9j/u1bqnvCEfJOwUhtlOARqs3+rkHYY13jYWTU97c= github.com/davecgh/go-spew v1.1.1/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38= github.com/lann/builder v0.0.0-20180802200727-47ae307949d0 h1:SOEGU9fKiNWd/HOJuq6+3iTQz8KNCLtVX6idSoTLdUw= github.com/lann/builder v0.0.0-20180802200727-47ae307949d0/go.mod h1:dXGbAdH5GtBTC4WfIxhKZfyBF/HBFgRZSWwZ9g/He9o= github.com/lann/ps v0.0.0-20150810152359-62de8c46ede0 h1:P6pPBnrTSX3DEVR4fDembhRWSsG5rVo6hYhAB/ADZrk= github.com/lann/ps v0.0.0-20150810152359-62de8c46ede0/go.mod h1:vmVJ0l/dxyfGW6FmdpVm2joNMFikkuWg0EoCKLGUMNw= github.com/pmezard/go-difflib v1.0.0 h1:4DBwDE0NGyQoBHbLQYPwSUPoCMWR5BEzIk/f1lZbAQM= github.com/pmezard/go-difflib v1.0.0/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZNVY4sRDYZ/4= github.com/stretchr/testify v1.2.2 h1:bSDNvY7ZPG5RlJ8otE/7V6gMiyenm9RtJ7IUVIAoJ1w= github.com/stretchr/testify v1.2.2/go.mod h1:a8OnRcib4nhh0OaRAV+Yts87kKdq0PP7pXfy6kDkUVs= ================================================ FILE: insert.go ================================================ package squirrel import ( "bytes" "database/sql" "errors" "fmt" "io" "sort" "strings" "github.com/lann/builder" ) type insertData struct { PlaceholderFormat PlaceholderFormat RunWith BaseRunner Prefixes []Sqlizer StatementKeyword string Options []string Into string Columns []string Values [][]interface{} Suffixes []Sqlizer Select *SelectBuilder } func (d *insertData) Exec() (sql.Result, error) { if d.RunWith == nil { return nil, RunnerNotSet } return ExecWith(d.RunWith, d) } func (d *insertData) Query() (*sql.Rows, error) { if d.RunWith == nil { return nil, RunnerNotSet } return QueryWith(d.RunWith, d) } func (d *insertData) 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 *insertData) ToSql() (sqlStr string, args []interface{}, err error) { if len(d.Into) == 0 { err = errors.New("insert statements must specify a table") return } if len(d.Values) == 0 && d.Select == nil { err = errors.New("insert statements must have at least one set of values or select clause") return } sql := &bytes.Buffer{} if len(d.Prefixes) > 0 { args, err = appendToSql(d.Prefixes, sql, " ", args) if err != nil { return } sql.WriteString(" ") } if d.StatementKeyword == "" { sql.WriteString("INSERT ") } else { sql.WriteString(d.StatementKeyword) sql.WriteString(" ") } if len(d.Options) > 0 { sql.WriteString(strings.Join(d.Options, " ")) sql.WriteString(" ") } sql.WriteString("INTO ") sql.WriteString(d.Into) sql.WriteString(" ") if len(d.Columns) > 0 { sql.WriteString("(") sql.WriteString(strings.Join(d.Columns, ",")) sql.WriteString(") ") } if d.Select != nil { args, err = d.appendSelectToSQL(sql, args) } else { args, err = d.appendValuesToSQL(sql, args) } if err != nil { return } if len(d.Suffixes) > 0 { sql.WriteString(" ") args, err = appendToSql(d.Suffixes, sql, " ", args) if err != nil { return } } sqlStr, err = d.PlaceholderFormat.ReplacePlaceholders(sql.String()) return } func (d *insertData) appendValuesToSQL(w io.Writer, args []interface{}) ([]interface{}, error) { if len(d.Values) == 0 { return args, errors.New("values for insert statements are not set") } io.WriteString(w, "VALUES ") valuesStrings := make([]string, len(d.Values)) for r, row := range d.Values { valueStrings := make([]string, len(row)) for v, val := range row { if vs, ok := val.(Sqlizer); ok { vsql, vargs, err := vs.ToSql() if err != nil { return nil, err } valueStrings[v] = vsql args = append(args, vargs...) } else { valueStrings[v] = "?" args = append(args, val) } } valuesStrings[r] = fmt.Sprintf("(%s)", strings.Join(valueStrings, ",")) } io.WriteString(w, strings.Join(valuesStrings, ",")) return args, nil } func (d *insertData) appendSelectToSQL(w io.Writer, args []interface{}) ([]interface{}, error) { if d.Select == nil { return args, errors.New("select clause for insert statements are not set") } selectClause, sArgs, err := d.Select.ToSql() if err != nil { return args, err } io.WriteString(w, selectClause) args = append(args, sArgs...) return args, nil } // Builder // InsertBuilder builds SQL INSERT statements. type InsertBuilder builder.Builder func init() { builder.Register(InsertBuilder{}, insertData{}) } // Format methods // PlaceholderFormat sets PlaceholderFormat (e.g. Question or Dollar) for the // query. func (b InsertBuilder) PlaceholderFormat(f PlaceholderFormat) InsertBuilder { return builder.Set(b, "PlaceholderFormat", f).(InsertBuilder) } // Runner methods // RunWith sets a Runner (like database/sql.DB) to be used with e.g. Exec. func (b InsertBuilder) RunWith(runner BaseRunner) InsertBuilder { return setRunWith(b, runner).(InsertBuilder) } // Exec builds and Execs the query with the Runner set by RunWith. func (b InsertBuilder) Exec() (sql.Result, error) { data := builder.GetStruct(b).(insertData) return data.Exec() } // Query builds and Querys the query with the Runner set by RunWith. func (b InsertBuilder) Query() (*sql.Rows, error) { data := builder.GetStruct(b).(insertData) return data.Query() } // QueryRow builds and QueryRows the query with the Runner set by RunWith. func (b InsertBuilder) QueryRow() RowScanner { data := builder.GetStruct(b).(insertData) return data.QueryRow() } // Scan is a shortcut for QueryRow().Scan. func (b InsertBuilder) Scan(dest ...interface{}) error { return b.QueryRow().Scan(dest...) } // SQL methods // ToSql builds the query into a SQL string and bound args. func (b InsertBuilder) ToSql() (string, []interface{}, error) { data := builder.GetStruct(b).(insertData) return data.ToSql() } // MustSql builds the query into a SQL string and bound args. // It panics if there are any errors. func (b InsertBuilder) MustSql() (string, []interface{}) { sql, args, err := b.ToSql() if err != nil { panic(err) } return sql, args } // Prefix adds an expression to the beginning of the query func (b InsertBuilder) Prefix(sql string, args ...interface{}) InsertBuilder { return b.PrefixExpr(Expr(sql, args...)) } // PrefixExpr adds an expression to the very beginning of the query func (b InsertBuilder) PrefixExpr(expr Sqlizer) InsertBuilder { return builder.Append(b, "Prefixes", expr).(InsertBuilder) } // Options adds keyword options before the INTO clause of the query. func (b InsertBuilder) Options(options ...string) InsertBuilder { return builder.Extend(b, "Options", options).(InsertBuilder) } // Into sets the INTO clause of the query. func (b InsertBuilder) Into(into string) InsertBuilder { return builder.Set(b, "Into", into).(InsertBuilder) } // Columns adds insert columns to the query. func (b InsertBuilder) Columns(columns ...string) InsertBuilder { return builder.Extend(b, "Columns", columns).(InsertBuilder) } // Values adds a single row's values to the query. func (b InsertBuilder) Values(values ...interface{}) InsertBuilder { return builder.Append(b, "Values", values).(InsertBuilder) } // Suffix adds an expression to the end of the query func (b InsertBuilder) Suffix(sql string, args ...interface{}) InsertBuilder { return b.SuffixExpr(Expr(sql, args...)) } // SuffixExpr adds an expression to the end of the query func (b InsertBuilder) SuffixExpr(expr Sqlizer) InsertBuilder { return builder.Append(b, "Suffixes", expr).(InsertBuilder) } // SetMap set columns and values for insert builder from a map of column name and value // note that it will reset all previous columns and values was set if any func (b InsertBuilder) SetMap(clauses map[string]interface{}) InsertBuilder { // Keep the columns in a consistent order by sorting the column key string. cols := make([]string, 0, len(clauses)) for col := range clauses { cols = append(cols, col) } sort.Strings(cols) vals := make([]interface{}, 0, len(clauses)) for _, col := range cols { vals = append(vals, clauses[col]) } b = builder.Set(b, "Columns", cols).(InsertBuilder) b = builder.Set(b, "Values", [][]interface{}{vals}).(InsertBuilder) return b } // Select set Select clause for insert query // If Values and Select are used, then Select has higher priority func (b InsertBuilder) Select(sb SelectBuilder) InsertBuilder { return builder.Set(b, "Select", &sb).(InsertBuilder) } func (b InsertBuilder) statementKeyword(keyword string) InsertBuilder { return builder.Set(b, "StatementKeyword", keyword).(InsertBuilder) } ================================================ FILE: insert_ctx.go ================================================ // +build go1.8 package squirrel import ( "context" "database/sql" "github.com/lann/builder" ) func (d *insertData) ExecContext(ctx context.Context) (sql.Result, error) { if d.RunWith == nil { return nil, RunnerNotSet } ctxRunner, ok := d.RunWith.(ExecerContext) if !ok { return nil, NoContextSupport } return ExecContextWith(ctx, ctxRunner, d) } func (d *insertData) QueryContext(ctx context.Context) (*sql.Rows, error) { if d.RunWith == nil { return nil, RunnerNotSet } ctxRunner, ok := d.RunWith.(QueryerContext) if !ok { return nil, NoContextSupport } return QueryContextWith(ctx, ctxRunner, d) } func (d *insertData) QueryRowContext(ctx context.Context) RowScanner { if d.RunWith == nil { return &Row{err: RunnerNotSet} } queryRower, ok := d.RunWith.(QueryRowerContext) if !ok { if _, ok := d.RunWith.(QueryerContext); !ok { return &Row{err: RunnerNotQueryRunner} } return &Row{err: NoContextSupport} } return QueryRowContextWith(ctx, queryRower, d) } // ExecContext builds and ExecContexts the query with the Runner set by RunWith. func (b InsertBuilder) ExecContext(ctx context.Context) (sql.Result, error) { data := builder.GetStruct(b).(insertData) return data.ExecContext(ctx) } // QueryContext builds and QueryContexts the query with the Runner set by RunWith. func (b InsertBuilder) QueryContext(ctx context.Context) (*sql.Rows, error) { data := builder.GetStruct(b).(insertData) return data.QueryContext(ctx) } // QueryRowContext builds and QueryRowContexts the query with the Runner set by RunWith. func (b InsertBuilder) QueryRowContext(ctx context.Context) RowScanner { data := builder.GetStruct(b).(insertData) return data.QueryRowContext(ctx) } // ScanContext is a shortcut for QueryRowContext().Scan. func (b InsertBuilder) ScanContext(ctx context.Context, dest ...interface{}) error { return b.QueryRowContext(ctx).Scan(dest...) } ================================================ FILE: insert_ctx_test.go ================================================ // +build go1.8 package squirrel import ( "testing" "github.com/stretchr/testify/assert" ) func TestInsertBuilderContextRunners(t *testing.T) { db := &DBStub{} b := Insert("test").Values(1).RunWith(db) expectedSql := "INSERT INTO test VALUES (?)" b.ExecContext(ctx) assert.Equal(t, expectedSql, db.LastExecSql) b.QueryContext(ctx) assert.Equal(t, expectedSql, db.LastQuerySql) b.QueryRowContext(ctx) assert.Equal(t, expectedSql, db.LastQueryRowSql) err := b.ScanContext(ctx) assert.NoError(t, err) } func TestInsertBuilderContextNoRunner(t *testing.T) { b := Insert("test").Values(1) _, err := b.ExecContext(ctx) assert.Equal(t, RunnerNotSet, err) _, err = b.QueryContext(ctx) assert.Equal(t, RunnerNotSet, err) err = b.ScanContext(ctx) assert.Equal(t, RunnerNotSet, err) } ================================================ FILE: insert_test.go ================================================ package squirrel import ( "testing" "github.com/stretchr/testify/assert" ) func TestInsertBuilderToSql(t *testing.T) { b := Insert(""). Prefix("WITH prefix AS ?", 0). Into("a"). Options("DELAYED", "IGNORE"). Columns("b", "c"). Values(1, 2). Values(3, Expr("? + 1", 4)). Suffix("RETURNING ?", 5) sql, args, err := b.ToSql() assert.NoError(t, err) expectedSQL := "WITH prefix AS ? " + "INSERT DELAYED IGNORE INTO a (b,c) VALUES (?,?),(?,? + 1) " + "RETURNING ?" assert.Equal(t, expectedSQL, sql) expectedArgs := []interface{}{0, 1, 2, 3, 4, 5} assert.Equal(t, expectedArgs, args) } func TestInsertBuilderToSqlErr(t *testing.T) { _, _, err := Insert("").Values(1).ToSql() assert.Error(t, err) _, _, err = Insert("x").ToSql() assert.Error(t, err) } func TestInsertBuilderMustSql(t *testing.T) { defer func() { if r := recover(); r == nil { t.Errorf("TestInsertBuilderMustSql should have panicked!") } }() Insert("").MustSql() } func TestInsertBuilderPlaceholders(t *testing.T) { b := Insert("test").Values(1, 2) sql, _, _ := b.PlaceholderFormat(Question).ToSql() assert.Equal(t, "INSERT INTO test VALUES (?,?)", sql) sql, _, _ = b.PlaceholderFormat(Dollar).ToSql() assert.Equal(t, "INSERT INTO test VALUES ($1,$2)", sql) } func TestInsertBuilderRunners(t *testing.T) { db := &DBStub{} b := Insert("test").Values(1).RunWith(db) expectedSQL := "INSERT INTO test VALUES (?)" b.Exec() assert.Equal(t, expectedSQL, db.LastExecSql) } func TestInsertBuilderNoRunner(t *testing.T) { b := Insert("test").Values(1) _, err := b.Exec() assert.Equal(t, RunnerNotSet, err) } func TestInsertBuilderSetMap(t *testing.T) { b := Insert("table").SetMap(Eq{"field1": 1, "field2": 2, "field3": 3}) sql, args, err := b.ToSql() assert.NoError(t, err) expectedSQL := "INSERT INTO table (field1,field2,field3) VALUES (?,?,?)" assert.Equal(t, expectedSQL, sql) expectedArgs := []interface{}{1, 2, 3} assert.Equal(t, expectedArgs, args) } func TestInsertBuilderSelect(t *testing.T) { sb := Select("field1").From("table1").Where(Eq{"field1": 1}) ib := Insert("table2").Columns("field1").Select(sb) sql, args, err := ib.ToSql() assert.NoError(t, err) expectedSQL := "INSERT INTO table2 (field1) SELECT field1 FROM table1 WHERE field1 = ?" assert.Equal(t, expectedSQL, sql) expectedArgs := []interface{}{1} assert.Equal(t, expectedArgs, args) } func TestInsertBuilderReplace(t *testing.T) { b := Replace("table").Values(1) expectedSQL := "REPLACE INTO table VALUES (?)" sql, _, err := b.ToSql() assert.NoError(t, err) assert.Equal(t, expectedSQL, sql) } ================================================ FILE: integration/doc.go ================================================ // This is a tests-only package. package integration ================================================ FILE: integration/go.mod ================================================ module github.com/Masterminds/squirrel/integration go 1.12 require ( github.com/Masterminds/squirrel v1.1.0 github.com/go-sql-driver/mysql v1.4.1 github.com/lib/pq v1.2.0 github.com/mattn/go-sqlite3 v1.13.0 github.com/stretchr/testify v1.4.0 google.golang.org/appengine v1.6.5 // indirect ) replace github.com/Masterminds/squirrel => ../ ================================================ FILE: integration/go.sum ================================================ github.com/Masterminds/squirrel v1.1.0 h1:baP1qLdoQCeTw3ifCdOq2dkYc6vGcmRdaociKLbEJXs= github.com/Masterminds/squirrel v1.1.0/go.mod h1:yaPeOnPG5ZRwL9oKdTsO/prlkPbXWZlRVMQ/gGlzIuA= github.com/davecgh/go-spew v1.1.0/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38= github.com/davecgh/go-spew v1.1.1 h1:vj9j/u1bqnvCEfJOwUhtlOARqs3+rkHYY13jYWTU97c= github.com/davecgh/go-spew v1.1.1/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38= github.com/go-sql-driver/mysql v1.4.1 h1:g24URVg0OFbNUTx9qqY1IRZ9D9z3iPyi5zKhQZpNwpA= github.com/go-sql-driver/mysql v1.4.1/go.mod h1:zAC/RDZ24gD3HViQzih4MyKcchzm+sOG5ZlKdlhCg5w= github.com/golang/protobuf v1.3.1/go.mod h1:6lQm79b+lXiMfvg/cZm0SGofjICqVBUtrP5yJMmIC1U= github.com/lann/builder v0.0.0-20180802200727-47ae307949d0 h1:SOEGU9fKiNWd/HOJuq6+3iTQz8KNCLtVX6idSoTLdUw= github.com/lann/builder v0.0.0-20180802200727-47ae307949d0/go.mod h1:dXGbAdH5GtBTC4WfIxhKZfyBF/HBFgRZSWwZ9g/He9o= github.com/lann/ps v0.0.0-20150810152359-62de8c46ede0 h1:P6pPBnrTSX3DEVR4fDembhRWSsG5rVo6hYhAB/ADZrk= github.com/lann/ps v0.0.0-20150810152359-62de8c46ede0/go.mod h1:vmVJ0l/dxyfGW6FmdpVm2joNMFikkuWg0EoCKLGUMNw= github.com/lib/pq v1.2.0 h1:LXpIM/LZ5xGFhOpXAQUIMM1HdyqzVYM13zNdjCEEcA0= github.com/lib/pq v1.2.0/go.mod h1:5WUZQaWbwv1U+lTReE5YruASi9Al49XbQIvNi/34Woo= github.com/mattn/go-sqlite3 v1.13.0 h1:LnJI81JidiW9r7pS/hXe6cFeO5EXNq7KbfvoJLRI69c= github.com/mattn/go-sqlite3 v1.13.0/go.mod h1:FPy6KqzDD04eiIsT53CuJW3U88zkxoIYsOqkbpncsNc= github.com/pmezard/go-difflib v1.0.0 h1:4DBwDE0NGyQoBHbLQYPwSUPoCMWR5BEzIk/f1lZbAQM= github.com/pmezard/go-difflib v1.0.0/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZNVY4sRDYZ/4= github.com/stretchr/objx v0.1.0/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+wExME= github.com/stretchr/testify v1.2.2/go.mod h1:a8OnRcib4nhh0OaRAV+Yts87kKdq0PP7pXfy6kDkUVs= github.com/stretchr/testify v1.4.0 h1:2E4SXV/wtOkTonXsotYi4li6zVWxYlZuYNCXe9XRJyk= github.com/stretchr/testify v1.4.0/go.mod h1:j7eGeouHqKxXV5pUuKE4zz7dFj8WfuZ+81PSLYec5m4= golang.org/x/crypto v0.0.0-20190308221718-c2843e01d9a2/go.mod h1:djNgcEr1/C05ACkg1iLfiJU5Ep61QUkGW8qpdssI0+w= golang.org/x/net v0.0.0-20190603091049-60506f45cf65/go.mod h1:HSz+uSET+XFnRR8LxR5pz3Of3rY3CfYBVs4xY44aLks= golang.org/x/sys v0.0.0-20190215142949-d0b11bdaac8a/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY= golang.org/x/text v0.3.0/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ= golang.org/x/text v0.3.2/go.mod h1:bEr9sfX3Q8Zfm5fL9x+3itogRgK3+ptLWKqgva+5dAk= golang.org/x/tools v0.0.0-20180917221912-90fa682c2a6e/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ= google.golang.org/appengine v1.6.5 h1:tycE03LOZYQNhDpS27tcQdAzLCVMaj7QT2SXxebnpCM= google.golang.org/appengine v1.6.5/go.mod h1:8WjMMxjGQR8xUklV/ARdw2HLXBOI7O7uCIDZVag1xfc= gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405 h1:yhCVgyC4o1eVCa2tZl7eS0r+SDo693bJlVdllGtEeKM= gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0= gopkg.in/yaml.v2 v2.2.2 h1:ZCJp+EgiOT7lHqUV2J862kp8Qj64Jo6az82+3Td9dZw= gopkg.in/yaml.v2 v2.2.2/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI= ================================================ FILE: integration/integration_test.go ================================================ package integration import ( "context" "database/sql" "flag" "fmt" "os" "testing" "github.com/stretchr/testify/assert" sqrl "github.com/Masterminds/squirrel" _ "github.com/go-sql-driver/mysql" _ "github.com/lib/pq" _ "github.com/mattn/go-sqlite3" ) const ( testSchema = ` CREATE TABLE squirrel_integration ( k INT, v TEXT )` testData = ` INSERT INTO squirrel_integration VALUES (1, 'foo'), (3, 'bar'), (2, 'foo'), (4, 'baz') ` ) var ( sb sqrl.StatementBuilderType ) func TestMain(m *testing.M) { var driver, dataSource string flag.StringVar(&driver, "driver", "", "integration database driver") flag.StringVar(&dataSource, "dataSource", "", "integration database data source") flag.Parse() if driver == "" { driver = "sqlite3" } if driver == "sqlite3" && dataSource == "" { dataSource = ":memory:" } db, err := sql.Open(driver, dataSource) if err != nil { fmt.Printf("error opening database: %v\n", err) os.Exit(-1) } _, err = db.Exec(testSchema) if err != nil { fmt.Printf("error creating test schema: %v\n", err) os.Exit(-2) } defer func() { _, err = db.Exec("DROP TABLE squirrel_integration") fmt.Printf("error removing test schema: %v\n", err) }() _, err = db.Exec(testData) if err != nil { fmt.Printf("error inserting test data: %v\n", err) os.Exit(-3) } sb = sqrl.StatementBuilder.RunWith(db) if driver == "postgres" { sb = sb.PlaceholderFormat(sqrl.Dollar) } os.Exit(m.Run()) } func assertVals(t *testing.T, s sqrl.SelectBuilder, expected ...string) { rows, err := s.Query() assert.NoError(t, err) defer rows.Close() vals := make([]string, len(expected)) for i := range vals { assert.True(t, rows.Next()) assert.NoError(t, rows.Scan(&vals[i])) } assert.False(t, rows.Next()) if expected != nil { assert.Equal(t, expected, vals) } } func TestSimpleSelect(t *testing.T) { assertVals( t, sb.Select("v").From("squirrel_integration"), "foo", "bar", "foo", "baz") } func TestEq(t *testing.T) { s := sb.Select("v").From("squirrel_integration") assertVals(t, s.Where(sqrl.Eq{"k": 4}), "baz") assertVals(t, s.Where(sqrl.NotEq{"k": 2}), "foo", "bar", "baz") assertVals(t, s.Where(sqrl.Eq{"k": []int{1, 4}}), "foo", "baz") assertVals(t, s.Where(sqrl.NotEq{"k": []int{1, 4}}), "bar", "foo") assertVals(t, s.Where(sqrl.Eq{"k": nil})) assertVals(t, s.Where(sqrl.NotEq{"k": nil}), "foo", "bar", "foo", "baz") assertVals(t, s.Where(sqrl.Eq{"k": []int{}})) assertVals(t, s.Where(sqrl.NotEq{"k": []int{}}), "foo", "bar", "foo", "baz") } func TestIneq(t *testing.T) { s := sb.Select("v").From("squirrel_integration") assertVals(t, s.Where(sqrl.Lt{"k": 3}), "foo", "foo") assertVals(t, s.Where(sqrl.Gt{"k": 3}), "baz") } func TestConj(t *testing.T) { s := sb.Select("v").From("squirrel_integration") assertVals(t, s.Where(sqrl.And{sqrl.Gt{"k": 1}, sqrl.Lt{"k": 4}}), "bar", "foo") assertVals(t, s.Where(sqrl.Or{sqrl.Gt{"k": 3}, sqrl.Lt{"k": 2}}), "foo", "baz") } func TestContext(t *testing.T) { s := sb.Select("v").From("squirrel_integration") ctx := context.Background() _, err := s.QueryContext(ctx) assert.NoError(t, err) } ================================================ FILE: part.go ================================================ package squirrel import ( "fmt" "io" ) type part struct { pred interface{} args []interface{} } func newPart(pred interface{}, args ...interface{}) Sqlizer { return &part{pred, args} } func (p part) ToSql() (sql string, args []interface{}, err error) { switch pred := p.pred.(type) { case nil: // no-op case Sqlizer: sql, args, err = nestedToSql(pred) case string: sql = pred args = p.args default: err = fmt.Errorf("expected string or Sqlizer, not %T", pred) } return } func nestedToSql(s Sqlizer) (string, []interface{}, error) { if raw, ok := s.(rawSqlizer); ok { return raw.toSqlRaw() } else { return s.ToSql() } } func appendToSql(parts []Sqlizer, w io.Writer, sep string, args []interface{}) ([]interface{}, error) { for i, p := range parts { partSql, partArgs, err := nestedToSql(p) if err != nil { return nil, err } else if len(partSql) == 0 { continue } if i > 0 { _, err := io.WriteString(w, sep) if err != nil { return nil, err } } _, err = io.WriteString(w, partSql) if err != nil { return nil, err } args = append(args, partArgs...) } return args, nil } ================================================ FILE: placeholder.go ================================================ package squirrel import ( "bytes" "fmt" "strings" ) // PlaceholderFormat is the interface that wraps the ReplacePlaceholders method. // // ReplacePlaceholders takes a SQL statement and replaces each question mark // placeholder with a (possibly different) SQL placeholder. type PlaceholderFormat interface { ReplacePlaceholders(sql string) (string, error) } type placeholderDebugger interface { debugPlaceholder() string } var ( // Question is a PlaceholderFormat instance that leaves placeholders as // question marks. Question = questionFormat{} // Dollar is a PlaceholderFormat instance that replaces placeholders with // dollar-prefixed positional placeholders (e.g. $1, $2, $3). Dollar = dollarFormat{} // Colon is a PlaceholderFormat instance that replaces placeholders with // colon-prefixed positional placeholders (e.g. :1, :2, :3). Colon = colonFormat{} // AtP is a PlaceholderFormat instance that replaces placeholders with // "@p"-prefixed positional placeholders (e.g. @p1, @p2, @p3). AtP = atpFormat{} ) type questionFormat struct{} func (questionFormat) ReplacePlaceholders(sql string) (string, error) { return sql, nil } func (questionFormat) debugPlaceholder() string { return "?" } type dollarFormat struct{} func (dollarFormat) ReplacePlaceholders(sql string) (string, error) { return replacePositionalPlaceholders(sql, "$") } func (dollarFormat) debugPlaceholder() string { return "$" } type colonFormat struct{} func (colonFormat) ReplacePlaceholders(sql string) (string, error) { return replacePositionalPlaceholders(sql, ":") } func (colonFormat) debugPlaceholder() string { return ":" } type atpFormat struct{} func (atpFormat) ReplacePlaceholders(sql string) (string, error) { return replacePositionalPlaceholders(sql, "@p") } func (atpFormat) debugPlaceholder() string { return "@p" } // Placeholders returns a string with count ? placeholders joined with commas. func Placeholders(count int) string { if count < 1 { return "" } return strings.Repeat(",?", count)[1:] } func replacePositionalPlaceholders(sql, prefix string) (string, error) { buf := &bytes.Buffer{} i := 0 for { p := strings.Index(sql, "?") if p == -1 { break } if len(sql[p:]) > 1 && sql[p:p+2] == "??" { // escape ?? => ? buf.WriteString(sql[:p]) buf.WriteString("?") if len(sql[p:]) == 1 { break } sql = sql[p+2:] } else { i++ buf.WriteString(sql[:p]) fmt.Fprintf(buf, "%s%d", prefix, i) sql = sql[p+1:] } } buf.WriteString(sql) return buf.String(), nil } ================================================ FILE: placeholder_test.go ================================================ package squirrel import ( "strings" "testing" "github.com/stretchr/testify/assert" ) func TestQuestion(t *testing.T) { sql := "x = ? AND y = ?" s, _ := Question.ReplacePlaceholders(sql) assert.Equal(t, sql, s) } func TestDollar(t *testing.T) { sql := "x = ? AND y = ?" s, _ := Dollar.ReplacePlaceholders(sql) assert.Equal(t, "x = $1 AND y = $2", s) } func TestColon(t *testing.T) { sql := "x = ? AND y = ?" s, _ := Colon.ReplacePlaceholders(sql) assert.Equal(t, "x = :1 AND y = :2", s) } func TestAtp(t *testing.T) { sql := "x = ? AND y = ?" s, _ := AtP.ReplacePlaceholders(sql) assert.Equal(t, "x = @p1 AND y = @p2", s) } func TestPlaceholders(t *testing.T) { assert.Equal(t, Placeholders(2), "?,?") } func TestEscapeDollar(t *testing.T) { sql := "SELECT uuid, \"data\" #> '{tags}' AS tags FROM nodes WHERE \"data\" -> 'tags' ??| array['?'] AND enabled = ?" s, _ := Dollar.ReplacePlaceholders(sql) assert.Equal(t, "SELECT uuid, \"data\" #> '{tags}' AS tags FROM nodes WHERE \"data\" -> 'tags' ?| array['$1'] AND enabled = $2", s) } func TestEscapeColon(t *testing.T) { sql := "SELECT uuid, \"data\" #> '{tags}' AS tags FROM nodes WHERE \"data\" -> 'tags' ??| array['?'] AND enabled = ?" s, _ := Colon.ReplacePlaceholders(sql) assert.Equal(t, "SELECT uuid, \"data\" #> '{tags}' AS tags FROM nodes WHERE \"data\" -> 'tags' ?| array[':1'] AND enabled = :2", s) } func TestEscapeAtp(t *testing.T) { sql := "SELECT uuid, \"data\" #> '{tags}' AS tags FROM nodes WHERE \"data\" -> 'tags' ??| array['?'] AND enabled = ?" s, _ := AtP.ReplacePlaceholders(sql) assert.Equal(t, "SELECT uuid, \"data\" #> '{tags}' AS tags FROM nodes WHERE \"data\" -> 'tags' ?| array['@p1'] AND enabled = @p2", s) } func BenchmarkPlaceholdersArray(b *testing.B) { var count = b.N placeholders := make([]string, count) for i := 0; i < count; i++ { placeholders[i] = "?" } var _ = strings.Join(placeholders, ",") } func BenchmarkPlaceholdersStrings(b *testing.B) { Placeholders(b.N) } ================================================ FILE: row.go ================================================ package squirrel // RowScanner is the interface that wraps the Scan method. // // Scan behaves like database/sql.Row.Scan. type RowScanner interface { Scan(...interface{}) error } // Row wraps database/sql.Row to let squirrel return new errors on Scan. type Row struct { RowScanner err error } // Scan returns Row.err or calls RowScanner.Scan. func (r *Row) Scan(dest ...interface{}) error { if r.err != nil { return r.err } return r.RowScanner.Scan(dest...) } ================================================ FILE: row_test.go ================================================ package squirrel import ( "fmt" "testing" "github.com/stretchr/testify/assert" ) type RowStub struct { Scanned bool } func (r *RowStub) Scan(_ ...interface{}) error { r.Scanned = true return nil } func TestRowScan(t *testing.T) { stub := &RowStub{} row := &Row{RowScanner: stub} err := row.Scan() assert.True(t, stub.Scanned, "row was not scanned") assert.NoError(t, err) } func TestRowScanErr(t *testing.T) { stub := &RowStub{} rowErr := fmt.Errorf("scan err") row := &Row{RowScanner: stub, err: rowErr} err := row.Scan() assert.False(t, stub.Scanned, "row was scanned") assert.Equal(t, rowErr, err) } ================================================ FILE: select.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.toSqlRaw() if err != nil { return } sqlStr, err = d.PlaceholderFormat.ReplacePlaceholders(sqlStr) return } func (d *selectData) toSqlRaw() (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) toSqlRaw() (string, []interface{}, error) { data := builder.GetStruct(b).(selectData) return data.toSqlRaw() } // MustSql builds the query into a SQL string and bound args. // It panics if there are any errors. func (b SelectBuilder) MustSql() (string, []interface{}) { sql, args, err := b.ToSql() if err != nil { panic(err) } return sql, args } // 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) } // RemoveColumns remove all columns from query. // Must add a new column with Column or Columns methods, otherwise // return a error. func (b SelectBuilder) RemoveColumns() SelectBuilder { return builder.Delete(b, "Columns").(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 " = ?", with the corresponding value // bound to the placeholder. If the value is nil, the expression will be " // IS NULL". If the value is an array or slice, the expression will be " 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) } ================================================ FILE: select_ctx.go ================================================ // +build go1.8 package squirrel import ( "context" "database/sql" "github.com/lann/builder" ) func (d *selectData) ExecContext(ctx context.Context) (sql.Result, error) { if d.RunWith == nil { return nil, RunnerNotSet } ctxRunner, ok := d.RunWith.(ExecerContext) if !ok { return nil, NoContextSupport } return ExecContextWith(ctx, ctxRunner, d) } func (d *selectData) QueryContext(ctx context.Context) (*sql.Rows, error) { if d.RunWith == nil { return nil, RunnerNotSet } ctxRunner, ok := d.RunWith.(QueryerContext) if !ok { return nil, NoContextSupport } return QueryContextWith(ctx, ctxRunner, d) } func (d *selectData) QueryRowContext(ctx context.Context) RowScanner { if d.RunWith == nil { return &Row{err: RunnerNotSet} } queryRower, ok := d.RunWith.(QueryRowerContext) if !ok { if _, ok := d.RunWith.(QueryerContext); !ok { return &Row{err: RunnerNotQueryRunner} } return &Row{err: NoContextSupport} } return QueryRowContextWith(ctx, queryRower, d) } // ExecContext builds and ExecContexts the query with the Runner set by RunWith. func (b SelectBuilder) ExecContext(ctx context.Context) (sql.Result, error) { data := builder.GetStruct(b).(selectData) return data.ExecContext(ctx) } // QueryContext builds and QueryContexts the query with the Runner set by RunWith. func (b SelectBuilder) QueryContext(ctx context.Context) (*sql.Rows, error) { data := builder.GetStruct(b).(selectData) return data.QueryContext(ctx) } // QueryRowContext builds and QueryRowContexts the query with the Runner set by RunWith. func (b SelectBuilder) QueryRowContext(ctx context.Context) RowScanner { data := builder.GetStruct(b).(selectData) return data.QueryRowContext(ctx) } // ScanContext is a shortcut for QueryRowContext().Scan. func (b SelectBuilder) ScanContext(ctx context.Context, dest ...interface{}) error { return b.QueryRowContext(ctx).Scan(dest...) } ================================================ FILE: select_ctx_test.go ================================================ // +build go1.8 package squirrel import ( "testing" "github.com/stretchr/testify/assert" ) func TestSelectBuilderContextRunners(t *testing.T) { db := &DBStub{} b := Select("test").RunWith(db) expectedSql := "SELECT test" b.ExecContext(ctx) assert.Equal(t, expectedSql, db.LastExecSql) b.QueryContext(ctx) assert.Equal(t, expectedSql, db.LastQuerySql) b.QueryRowContext(ctx) assert.Equal(t, expectedSql, db.LastQueryRowSql) err := b.ScanContext(ctx) assert.NoError(t, err) } func TestSelectBuilderContextNoRunner(t *testing.T) { b := Select("test") _, err := b.ExecContext(ctx) assert.Equal(t, RunnerNotSet, err) _, err = b.QueryContext(ctx) assert.Equal(t, RunnerNotSet, err) err = b.ScanContext(ctx) assert.Equal(t, RunnerNotSet, err) } ================================================ FILE: select_test.go ================================================ package squirrel import ( "database/sql" "fmt" "log" "testing" "time" "github.com/stretchr/testify/assert" ) func TestSelectBuilderToSql(t *testing.T) { subQ := Select("aa", "bb").From("dd") b := Select("a", "b"). Prefix("WITH prefix AS ?", 0). Distinct(). Columns("c"). Column("IF(d IN ("+Placeholders(3)+"), 1, 0) as stat_column", 1, 2, 3). Column(Expr("a > ?", 100)). Column(Alias(Eq{"b": []int{101, 102, 103}}, "b_alias")). Column(Alias(subQ, "subq")). From("e"). JoinClause("CROSS JOIN j1"). Join("j2"). LeftJoin("j3"). RightJoin("j4"). InnerJoin("j5"). CrossJoin("j6"). Where("f = ?", 4). Where(Eq{"g": 5}). Where(map[string]interface{}{"h": 6}). Where(Eq{"i": []int{7, 8, 9}}). Where(Or{Expr("j = ?", 10), And{Eq{"k": 11}, Expr("true")}}). GroupBy("l"). Having("m = n"). OrderByClause("? DESC", 1). OrderBy("o ASC", "p DESC"). Limit(12). Offset(13). Suffix("FETCH FIRST ? ROWS ONLY", 14) sql, args, err := b.ToSql() assert.NoError(t, err) expectedSql := "WITH prefix AS ? " + "SELECT DISTINCT a, b, c, IF(d IN (?,?,?), 1, 0) as stat_column, a > ?, " + "(b IN (?,?,?)) AS b_alias, " + "(SELECT aa, bb FROM dd) AS subq " + "FROM e " + "CROSS JOIN j1 JOIN j2 LEFT JOIN j3 RIGHT JOIN j4 INNER JOIN j5 CROSS JOIN j6 " + "WHERE f = ? AND g = ? AND h = ? AND i IN (?,?,?) AND (j = ? OR (k = ? AND true)) " + "GROUP BY l HAVING m = n ORDER BY ? DESC, o ASC, p DESC LIMIT 12 OFFSET 13 " + "FETCH FIRST ? ROWS ONLY" assert.Equal(t, expectedSql, sql) expectedArgs := []interface{}{0, 1, 2, 3, 100, 101, 102, 103, 4, 5, 6, 7, 8, 9, 10, 11, 1, 14} assert.Equal(t, expectedArgs, args) } func TestSelectBuilderFromSelect(t *testing.T) { subQ := Select("c").From("d").Where(Eq{"i": 0}) b := Select("a", "b").FromSelect(subQ, "subq") sql, args, err := b.ToSql() assert.NoError(t, err) expectedSql := "SELECT a, b FROM (SELECT c FROM d WHERE i = ?) AS subq" assert.Equal(t, expectedSql, sql) expectedArgs := []interface{}{0} assert.Equal(t, expectedArgs, args) } func TestSelectBuilderFromSelectNestedDollarPlaceholders(t *testing.T) { subQ := Select("c"). From("t"). Where(Gt{"c": 1}). PlaceholderFormat(Dollar) b := Select("c"). FromSelect(subQ, "subq"). Where(Lt{"c": 2}). PlaceholderFormat(Dollar) sql, args, err := b.ToSql() assert.NoError(t, err) expectedSql := "SELECT c FROM (SELECT c FROM t WHERE c > $1) AS subq WHERE c < $2" assert.Equal(t, expectedSql, sql) expectedArgs := []interface{}{1, 2} assert.Equal(t, expectedArgs, args) } func TestSelectBuilderToSqlErr(t *testing.T) { _, _, err := Select().From("x").ToSql() assert.Error(t, err) } func TestSelectBuilderPlaceholders(t *testing.T) { b := Select("test").Where("x = ? AND y = ?") sql, _, _ := b.PlaceholderFormat(Question).ToSql() assert.Equal(t, "SELECT test WHERE x = ? AND y = ?", sql) sql, _, _ = b.PlaceholderFormat(Dollar).ToSql() assert.Equal(t, "SELECT test WHERE x = $1 AND y = $2", sql) sql, _, _ = b.PlaceholderFormat(Colon).ToSql() assert.Equal(t, "SELECT test WHERE x = :1 AND y = :2", sql) sql, _, _ = b.PlaceholderFormat(AtP).ToSql() assert.Equal(t, "SELECT test WHERE x = @p1 AND y = @p2", sql) } func TestSelectBuilderRunners(t *testing.T) { db := &DBStub{} b := Select("test").RunWith(db) expectedSql := "SELECT test" b.Exec() assert.Equal(t, expectedSql, db.LastExecSql) b.Query() assert.Equal(t, expectedSql, db.LastQuerySql) b.QueryRow() assert.Equal(t, expectedSql, db.LastQueryRowSql) err := b.Scan() assert.NoError(t, err) } func TestSelectBuilderNoRunner(t *testing.T) { b := Select("test") _, err := b.Exec() assert.Equal(t, RunnerNotSet, err) _, err = b.Query() assert.Equal(t, RunnerNotSet, err) err = b.Scan() assert.Equal(t, RunnerNotSet, err) } func TestSelectBuilderSimpleJoin(t *testing.T) { expectedSql := "SELECT * FROM bar JOIN baz ON bar.foo = baz.foo" expectedArgs := []interface{}(nil) b := Select("*").From("bar").Join("baz ON bar.foo = baz.foo") sql, args, err := b.ToSql() assert.NoError(t, err) assert.Equal(t, expectedSql, sql) assert.Equal(t, args, expectedArgs) } func TestSelectBuilderParamJoin(t *testing.T) { expectedSql := "SELECT * FROM bar JOIN baz ON bar.foo = baz.foo AND baz.foo = ?" expectedArgs := []interface{}{42} b := Select("*").From("bar").Join("baz ON bar.foo = baz.foo AND baz.foo = ?", 42) sql, args, err := b.ToSql() assert.NoError(t, err) assert.Equal(t, expectedSql, sql) assert.Equal(t, args, expectedArgs) } func TestSelectBuilderNestedSelectJoin(t *testing.T) { expectedSql := "SELECT * FROM bar JOIN ( SELECT * FROM baz WHERE foo = ? ) r ON bar.foo = r.foo" expectedArgs := []interface{}{42} nestedSelect := Select("*").From("baz").Where("foo = ?", 42) b := Select("*").From("bar").JoinClause(nestedSelect.Prefix("JOIN (").Suffix(") r ON bar.foo = r.foo")) sql, args, err := b.ToSql() assert.NoError(t, err) assert.Equal(t, expectedSql, sql) assert.Equal(t, args, expectedArgs) } func TestSelectWithOptions(t *testing.T) { sql, _, err := Select("*").From("foo").Distinct().Options("SQL_NO_CACHE").ToSql() assert.NoError(t, err) assert.Equal(t, "SELECT DISTINCT SQL_NO_CACHE * FROM foo", sql) } func TestSelectWithRemoveLimit(t *testing.T) { sql, _, err := Select("*").From("foo").Limit(10).RemoveLimit().ToSql() assert.NoError(t, err) assert.Equal(t, "SELECT * FROM foo", sql) } func TestSelectWithRemoveOffset(t *testing.T) { sql, _, err := Select("*").From("foo").Offset(10).RemoveOffset().ToSql() assert.NoError(t, err) assert.Equal(t, "SELECT * FROM foo", sql) } func TestSelectBuilderNestedSelectDollar(t *testing.T) { nestedBuilder := StatementBuilder.PlaceholderFormat(Dollar).Select("*").Prefix("NOT EXISTS ("). From("bar").Where("y = ?", 42).Suffix(")") outerSql, _, err := StatementBuilder.PlaceholderFormat(Dollar).Select("*"). From("foo").Where("x = ?").Where(nestedBuilder).ToSql() assert.NoError(t, err) assert.Equal(t, "SELECT * FROM foo WHERE x = $1 AND NOT EXISTS ( SELECT * FROM bar WHERE y = $2 )", outerSql) } func TestSelectBuilderMustSql(t *testing.T) { defer func() { if r := recover(); r == nil { t.Errorf("TestSelectBuilderMustSql should have panicked!") } }() // This function should cause a panic Select().From("foo").MustSql() } func TestSelectWithoutWhereClause(t *testing.T) { sql, _, err := Select("*").From("users").ToSql() assert.NoError(t, err) assert.Equal(t, "SELECT * FROM users", sql) } func TestSelectWithNilWhereClause(t *testing.T) { sql, _, err := Select("*").From("users").Where(nil).ToSql() assert.NoError(t, err) assert.Equal(t, "SELECT * FROM users", sql) } func TestSelectWithEmptyStringWhereClause(t *testing.T) { sql, _, err := Select("*").From("users").Where("").ToSql() assert.NoError(t, err) assert.Equal(t, "SELECT * FROM users", sql) } func TestSelectSubqueryPlaceholderNumbering(t *testing.T) { subquery := Select("a").Where("b = ?", 1).PlaceholderFormat(Dollar) with := subquery.Prefix("WITH a AS (").Suffix(")") sql, args, err := Select("*"). PrefixExpr(with). FromSelect(subquery, "q"). Where("c = ?", 2). PlaceholderFormat(Dollar). ToSql() assert.NoError(t, err) expectedSql := "WITH a AS ( SELECT a WHERE b = $1 ) SELECT * FROM (SELECT a WHERE b = $2) AS q WHERE c = $3" assert.Equal(t, expectedSql, sql) assert.Equal(t, []interface{}{1, 1, 2}, args) } func TestSelectSubqueryInConjunctionPlaceholderNumbering(t *testing.T) { subquery := Select("a").Where(Eq{"b": 1}).Prefix("EXISTS(").Suffix(")").PlaceholderFormat(Dollar) sql, args, err := Select("*"). Where(Or{subquery}). Where("c = ?", 2). PlaceholderFormat(Dollar). ToSql() assert.NoError(t, err) expectedSql := "SELECT * WHERE (EXISTS( SELECT a WHERE b = $1 )) AND c = $2" assert.Equal(t, expectedSql, sql) assert.Equal(t, []interface{}{1, 2}, args) } func TestSelectJoinClausePlaceholderNumbering(t *testing.T) { subquery := Select("a").Where(Eq{"b": 2}).PlaceholderFormat(Dollar) sql, args, err := Select("t1.a"). From("t1"). Where(Eq{"a": 1}). JoinClause(subquery.Prefix("JOIN (").Suffix(") t2 ON (t1.a = t2.a)")). PlaceholderFormat(Dollar). ToSql() assert.NoError(t, err) expectedSql := "SELECT t1.a FROM t1 JOIN ( SELECT a WHERE b = $1 ) t2 ON (t1.a = t2.a) WHERE a = $2" assert.Equal(t, expectedSql, sql) assert.Equal(t, []interface{}{2, 1}, args) } func ExampleSelect() { Select("id", "created", "first_name").From("users") // ... continue building up your query // sql methods in select columns are ok Select("first_name", "count(*)").From("users") // column aliases are ok too Select("first_name", "count(*) as n_users").From("users") } func ExampleSelectBuilder_From() { Select("id", "created", "first_name").From("users") // ... continue building up your query } func ExampleSelectBuilder_Where() { companyId := 20 Select("id", "created", "first_name").From("users").Where("company = ?", companyId) } func ExampleSelectBuilder_Where_helpers() { companyId := 20 Select("id", "created", "first_name").From("users").Where(Eq{ "company": companyId, }) Select("id", "created", "first_name").From("users").Where(GtOrEq{ "created": time.Now().AddDate(0, 0, -7), }) Select("id", "created", "first_name").From("users").Where(And{ GtOrEq{ "created": time.Now().AddDate(0, 0, -7), }, Eq{ "company": companyId, }, }) } func ExampleSelectBuilder_Where_multiple() { companyId := 20 // multiple where's are ok Select("id", "created", "first_name"). From("users"). Where("company = ?", companyId). Where(GtOrEq{ "created": time.Now().AddDate(0, 0, -7), }) } func ExampleSelectBuilder_FromSelect() { usersByCompany := Select("company", "count(*) as n_users").From("users").GroupBy("company") query := Select("company.id", "company.name", "users_by_company.n_users"). FromSelect(usersByCompany, "users_by_company"). Join("company on company.id = users_by_company.company") sql, _, _ := query.ToSql() fmt.Println(sql) // Output: SELECT company.id, company.name, users_by_company.n_users FROM (SELECT company, count(*) as n_users FROM users GROUP BY company) AS users_by_company JOIN company on company.id = users_by_company.company } func ExampleSelectBuilder_Columns() { query := Select("id").Columns("created", "first_name").From("users") sql, _, _ := query.ToSql() fmt.Println(sql) // Output: SELECT id, created, first_name FROM users } func ExampleSelectBuilder_Columns_order() { // out of order is ok too query := Select("id").Columns("created").From("users").Columns("first_name") sql, _, _ := query.ToSql() fmt.Println(sql) // Output: SELECT id, created, first_name FROM users } func ExampleSelectBuilder_Scan() { var db *sql.DB query := Select("id", "created", "first_name").From("users") query = query.RunWith(db) var id int var created time.Time var firstName string if err := query.Scan(&id, &created, &firstName); err != nil { log.Println(err) return } } func ExampleSelectBuilder_ScanContext() { var db *sql.DB query := Select("id", "created", "first_name").From("users") query = query.RunWith(db) var id int var created time.Time var firstName string if err := query.ScanContext(ctx, &id, &created, &firstName); err != nil { log.Println(err) return } } func ExampleSelectBuilder_RunWith() { var db *sql.DB query := Select("id", "created", "first_name").From("users").RunWith(db) var id int var created time.Time var firstName string if err := query.Scan(&id, &created, &firstName); err != nil { log.Println(err) return } } func ExampleSelectBuilder_ToSql() { var db *sql.DB query := Select("id", "created", "first_name").From("users") sql, args, err := query.ToSql() if err != nil { log.Println(err) return } rows, err := db.Query(sql, args...) if err != nil { log.Println(err) return } defer rows.Close() for rows.Next() { // scan... } } func TestRemoveColumns(t *testing.T) { query := Select("id"). From("users"). RemoveColumns() query = query.Columns("name") sql, _, err := query.ToSql() assert.NoError(t, err) assert.Equal(t, "SELECT name FROM users", sql) } ================================================ FILE: squirrel.go ================================================ // Package squirrel provides a fluent SQL generator. // // See https://github.com/Masterminds/squirrel for examples. package squirrel import ( "bytes" "database/sql" "fmt" "strings" "github.com/lann/builder" ) // Sqlizer is the interface that wraps the ToSql method. // // ToSql returns a SQL representation of the Sqlizer, along with a slice of args // as passed to e.g. database/sql.Exec. It can also return an error. type Sqlizer interface { ToSql() (string, []interface{}, error) } // rawSqlizer is expected to do what Sqlizer does, but without finalizing placeholders. // This is useful for nested queries. type rawSqlizer interface { toSqlRaw() (string, []interface{}, error) } // Execer is the interface that wraps the Exec method. // // Exec executes the given query as implemented by database/sql.Exec. type Execer interface { Exec(query string, args ...interface{}) (sql.Result, error) } // Queryer is the interface that wraps the Query method. // // Query executes the given query as implemented by database/sql.Query. type Queryer interface { Query(query string, args ...interface{}) (*sql.Rows, error) } // QueryRower is the interface that wraps the QueryRow method. // // QueryRow executes the given query as implemented by database/sql.QueryRow. type QueryRower interface { QueryRow(query string, args ...interface{}) RowScanner } // BaseRunner groups the Execer and Queryer interfaces. type BaseRunner interface { Execer Queryer } // Runner groups the Execer, Queryer, and QueryRower interfaces. type Runner interface { Execer Queryer QueryRower } // WrapStdSql wraps a type implementing the standard SQL interface with methods that // squirrel expects. func WrapStdSql(stdSql StdSql) Runner { return &stdsqlRunner{stdSql} } // StdSql encompasses the standard methods of the *sql.DB type, and other types that // wrap these methods. type StdSql interface { Query(string, ...interface{}) (*sql.Rows, error) QueryRow(string, ...interface{}) *sql.Row Exec(string, ...interface{}) (sql.Result, error) } type stdsqlRunner struct { StdSql } func (r *stdsqlRunner) QueryRow(query string, args ...interface{}) RowScanner { return r.StdSql.QueryRow(query, args...) } func setRunWith(b interface{}, runner BaseRunner) interface{} { switch r := runner.(type) { case StdSqlCtx: runner = WrapStdSqlCtx(r) case StdSql: runner = WrapStdSql(r) } return builder.Set(b, "RunWith", runner) } // RunnerNotSet is returned by methods that need a Runner if it isn't set. var RunnerNotSet = fmt.Errorf("cannot run; no Runner set (RunWith)") // RunnerNotQueryRunner is returned by QueryRow if the RunWith value doesn't implement QueryRower. var RunnerNotQueryRunner = fmt.Errorf("cannot QueryRow; Runner is not a QueryRower") // ExecWith Execs the SQL returned by s with db. func ExecWith(db Execer, s Sqlizer) (res sql.Result, err error) { query, args, err := s.ToSql() if err != nil { return } return db.Exec(query, args...) } // QueryWith Querys the SQL returned by s with db. func QueryWith(db Queryer, s Sqlizer) (rows *sql.Rows, err error) { query, args, err := s.ToSql() if err != nil { return } return db.Query(query, args...) } // QueryRowWith QueryRows the SQL returned by s with db. func QueryRowWith(db QueryRower, s Sqlizer) RowScanner { query, args, err := s.ToSql() return &Row{RowScanner: db.QueryRow(query, args...), err: err} } // DebugSqlizer calls ToSql on s and shows the approximate SQL to be executed // // If ToSql returns an error, the result of this method will look like: // "[ToSql error: %s]" or "[DebugSqlizer error: %s]" // // IMPORTANT: As its name suggests, this function should only be used for // debugging. While the string result *might* be valid SQL, this function does // not try very hard to ensure it. Additionally, executing the output of this // function with any untrusted user input is certainly insecure. func DebugSqlizer(s Sqlizer) string { sql, args, err := s.ToSql() if err != nil { return fmt.Sprintf("[ToSql error: %s]", err) } var placeholder string downCast, ok := s.(placeholderDebugger) if !ok { placeholder = "?" } else { placeholder = downCast.debugPlaceholder() } // TODO: dedupe this with placeholder.go buf := &bytes.Buffer{} i := 0 for { p := strings.Index(sql, placeholder) if p == -1 { break } if len(sql[p:]) > 1 && sql[p:p+2] == "??" { // escape ?? => ? buf.WriteString(sql[:p]) buf.WriteString("?") if len(sql[p:]) == 1 { break } sql = sql[p+2:] } else { if i+1 > len(args) { return fmt.Sprintf( "[DebugSqlizer error: too many placeholders in %#v for %d args]", sql, len(args)) } buf.WriteString(sql[:p]) fmt.Fprintf(buf, "'%v'", args[i]) // advance our sql string "cursor" beyond the arg we placed sql = sql[p+1:] i++ } } if i < len(args) { return fmt.Sprintf( "[DebugSqlizer error: not enough placeholders in %#v for %d args]", sql, len(args)) } // "append" any remaning sql that won't need interpolating buf.WriteString(sql) return buf.String() } ================================================ FILE: squirrel_ctx.go ================================================ // +build go1.8 package squirrel import ( "context" "database/sql" "errors" ) // NoContextSupport is returned if a db doesn't support Context. var NoContextSupport = errors.New("DB does not support Context") // ExecerContext is the interface that wraps the ExecContext method. // // Exec executes the given query as implemented by database/sql.ExecContext. type ExecerContext interface { ExecContext(ctx context.Context, query string, args ...interface{}) (sql.Result, error) } // QueryerContext is the interface that wraps the QueryContext method. // // QueryContext executes the given query as implemented by database/sql.QueryContext. type QueryerContext interface { QueryContext(ctx context.Context, query string, args ...interface{}) (*sql.Rows, error) } // QueryRowerContext is the interface that wraps the QueryRowContext method. // // QueryRowContext executes the given query as implemented by database/sql.QueryRowContext. type QueryRowerContext interface { QueryRowContext(ctx context.Context, query string, args ...interface{}) RowScanner } // RunnerContext groups the Runner interface, along with the Context versions of each of // its methods type RunnerContext interface { Runner QueryerContext QueryRowerContext ExecerContext } // WrapStdSqlCtx wraps a type implementing the standard SQL interface plus the context // versions of the methods with methods that squirrel expects. func WrapStdSqlCtx(stdSqlCtx StdSqlCtx) RunnerContext { return &stdsqlCtxRunner{stdSqlCtx} } // StdSqlCtx encompasses the standard methods of the *sql.DB type, along with the Context // versions of those methods, and other types that wrap these methods. type StdSqlCtx interface { StdSql QueryContext(context.Context, string, ...interface{}) (*sql.Rows, error) QueryRowContext(context.Context, string, ...interface{}) *sql.Row ExecContext(context.Context, string, ...interface{}) (sql.Result, error) } type stdsqlCtxRunner struct { StdSqlCtx } func (r *stdsqlCtxRunner) QueryRow(query string, args ...interface{}) RowScanner { return r.StdSqlCtx.QueryRow(query, args...) } func (r *stdsqlCtxRunner) QueryRowContext(ctx context.Context, query string, args ...interface{}) RowScanner { return r.StdSqlCtx.QueryRowContext(ctx, query, args...) } // ExecContextWith ExecContexts the SQL returned by s with db. func ExecContextWith(ctx context.Context, db ExecerContext, s Sqlizer) (res sql.Result, err error) { query, args, err := s.ToSql() if err != nil { return } return db.ExecContext(ctx, query, args...) } // QueryContextWith QueryContexts the SQL returned by s with db. func QueryContextWith(ctx context.Context, db QueryerContext, s Sqlizer) (rows *sql.Rows, err error) { query, args, err := s.ToSql() if err != nil { return } return db.QueryContext(ctx, query, args...) } // QueryRowContextWith QueryRowContexts the SQL returned by s with db. func QueryRowContextWith(ctx context.Context, db QueryRowerContext, s Sqlizer) RowScanner { query, args, err := s.ToSql() return &Row{RowScanner: db.QueryRowContext(ctx, query, args...), err: err} } ================================================ FILE: squirrel_ctx_test.go ================================================ // +build go1.8 package squirrel import ( "context" "database/sql" "testing" "github.com/stretchr/testify/assert" ) func (s *DBStub) PrepareContext(ctx context.Context, query string) (*sql.Stmt, error) { s.LastPrepareSql = query s.PrepareCount++ return nil, nil } func (s *DBStub) ExecContext(ctx context.Context, query string, args ...interface{}) (sql.Result, error) { s.LastExecSql = query s.LastExecArgs = args return nil, nil } func (s *DBStub) QueryContext(ctx context.Context, query string, args ...interface{}) (*sql.Rows, error) { s.LastQuerySql = query s.LastQueryArgs = args return nil, nil } func (s *DBStub) QueryRowContext(ctx context.Context, query string, args ...interface{}) RowScanner { s.LastQueryRowSql = query s.LastQueryRowArgs = args return &Row{RowScanner: &RowStub{}} } var ctx = context.Background() func TestExecContextWith(t *testing.T) { db := &DBStub{} ExecContextWith(ctx, db, sqlizer) assert.Equal(t, sqlStr, db.LastExecSql) } func TestQueryContextWith(t *testing.T) { db := &DBStub{} QueryContextWith(ctx, db, sqlizer) assert.Equal(t, sqlStr, db.LastQuerySql) } func TestQueryRowContextWith(t *testing.T) { db := &DBStub{} QueryRowContextWith(ctx, db, sqlizer) assert.Equal(t, sqlStr, db.LastQueryRowSql) } ================================================ FILE: squirrel_test.go ================================================ package squirrel import ( "database/sql" "fmt" "strings" "testing" "github.com/stretchr/testify/assert" ) type DBStub struct { err error LastPrepareSql string PrepareCount int LastExecSql string LastExecArgs []interface{} LastQuerySql string LastQueryArgs []interface{} LastQueryRowSql string LastQueryRowArgs []interface{} } var StubError = fmt.Errorf("this is a stub; this is only a stub") func (s *DBStub) Prepare(query string) (*sql.Stmt, error) { s.LastPrepareSql = query s.PrepareCount++ return nil, nil } func (s *DBStub) Exec(query string, args ...interface{}) (sql.Result, error) { s.LastExecSql = query s.LastExecArgs = args return nil, nil } func (s *DBStub) Query(query string, args ...interface{}) (*sql.Rows, error) { s.LastQuerySql = query s.LastQueryArgs = args return nil, nil } func (s *DBStub) QueryRow(query string, args ...interface{}) RowScanner { s.LastQueryRowSql = query s.LastQueryRowArgs = args return &Row{RowScanner: &RowStub{}} } var sqlizer = Select("test") var sqlStr = "SELECT test" func TestExecWith(t *testing.T) { db := &DBStub{} ExecWith(db, sqlizer) assert.Equal(t, sqlStr, db.LastExecSql) } func TestQueryWith(t *testing.T) { db := &DBStub{} QueryWith(db, sqlizer) assert.Equal(t, sqlStr, db.LastQuerySql) } func TestQueryRowWith(t *testing.T) { db := &DBStub{} QueryRowWith(db, sqlizer) assert.Equal(t, sqlStr, db.LastQueryRowSql) } func TestWithToSqlErr(t *testing.T) { db := &DBStub{} sqlizer := Select() _, err := ExecWith(db, sqlizer) assert.Error(t, err) _, err = QueryWith(db, sqlizer) assert.Error(t, err) err = QueryRowWith(db, sqlizer).Scan() assert.Error(t, err) } var testDebugUpdateSQL = Update("table").SetMap(Eq{"x": 1, "y": "val"}) var expectedDebugUpateSQL = "UPDATE table SET x = '1', y = 'val'" func TestDebugSqlizerUpdateColon(t *testing.T) { testDebugUpdateSQL.PlaceholderFormat(Colon) assert.Equal(t, expectedDebugUpateSQL, DebugSqlizer(testDebugUpdateSQL)) } func TestDebugSqlizerUpdateAtp(t *testing.T) { testDebugUpdateSQL.PlaceholderFormat(AtP) assert.Equal(t, expectedDebugUpateSQL, DebugSqlizer(testDebugUpdateSQL)) } func TestDebugSqlizerUpdateDollar(t *testing.T) { testDebugUpdateSQL.PlaceholderFormat(Dollar) assert.Equal(t, expectedDebugUpateSQL, DebugSqlizer(testDebugUpdateSQL)) } func TestDebugSqlizerUpdateQuestion(t *testing.T) { testDebugUpdateSQL.PlaceholderFormat(Question) assert.Equal(t, expectedDebugUpateSQL, DebugSqlizer(testDebugUpdateSQL)) } var testDebugDeleteSQL = Delete("table").Where(And{ Eq{"column": "val"}, Eq{"other": 1}, }) var expectedDebugDeleteSQL = "DELETE FROM table WHERE (column = 'val' AND other = '1')" func TestDebugSqlizerDeleteColon(t *testing.T) { testDebugDeleteSQL.PlaceholderFormat(Colon) assert.Equal(t, expectedDebugDeleteSQL, DebugSqlizer(testDebugDeleteSQL)) } func TestDebugSqlizerDeleteAtp(t *testing.T) { testDebugDeleteSQL.PlaceholderFormat(AtP) assert.Equal(t, expectedDebugDeleteSQL, DebugSqlizer(testDebugDeleteSQL)) } func TestDebugSqlizerDeleteDollar(t *testing.T) { testDebugDeleteSQL.PlaceholderFormat(Dollar) assert.Equal(t, expectedDebugDeleteSQL, DebugSqlizer(testDebugDeleteSQL)) } func TestDebugSqlizerDeleteQuestion(t *testing.T) { testDebugDeleteSQL.PlaceholderFormat(Question) assert.Equal(t, expectedDebugDeleteSQL, DebugSqlizer(testDebugDeleteSQL)) } var testDebugInsertSQL = Insert("table").Values(1, "test") var expectedDebugInsertSQL = "INSERT INTO table VALUES ('1','test')" func TestDebugSqlizerInsertColon(t *testing.T) { testDebugInsertSQL.PlaceholderFormat(Colon) assert.Equal(t, expectedDebugInsertSQL, DebugSqlizer(testDebugInsertSQL)) } func TestDebugSqlizerInsertAtp(t *testing.T) { testDebugInsertSQL.PlaceholderFormat(AtP) assert.Equal(t, expectedDebugInsertSQL, DebugSqlizer(testDebugInsertSQL)) } func TestDebugSqlizerInsertDollar(t *testing.T) { testDebugInsertSQL.PlaceholderFormat(Dollar) assert.Equal(t, expectedDebugInsertSQL, DebugSqlizer(testDebugInsertSQL)) } func TestDebugSqlizerInsertQuestion(t *testing.T) { testDebugInsertSQL.PlaceholderFormat(Question) assert.Equal(t, expectedDebugInsertSQL, DebugSqlizer(testDebugInsertSQL)) } var testDebugSelectSQL = Select("*").From("table").Where(And{ Eq{"column": "val"}, Eq{"other": 1}, }) var expectedDebugSelectSQL = "SELECT * FROM table WHERE (column = 'val' AND other = '1')" func TestDebugSqlizerSelectColon(t *testing.T) { testDebugSelectSQL.PlaceholderFormat(Colon) assert.Equal(t, expectedDebugSelectSQL, DebugSqlizer(testDebugSelectSQL)) } func TestDebugSqlizerSelectAtp(t *testing.T) { testDebugSelectSQL.PlaceholderFormat(AtP) assert.Equal(t, expectedDebugSelectSQL, DebugSqlizer(testDebugSelectSQL)) } func TestDebugSqlizerSelectDollar(t *testing.T) { testDebugSelectSQL.PlaceholderFormat(Dollar) assert.Equal(t, expectedDebugSelectSQL, DebugSqlizer(testDebugSelectSQL)) } func TestDebugSqlizerSelectQuestion(t *testing.T) { testDebugSelectSQL.PlaceholderFormat(Question) assert.Equal(t, expectedDebugSelectSQL, DebugSqlizer(testDebugSelectSQL)) } func TestDebugSqlizer(t *testing.T) { sqlizer := Expr("x = ? AND y = ? AND z = '??'", 1, "text") expectedDebug := "x = '1' AND y = 'text' AND z = '?'" assert.Equal(t, expectedDebug, DebugSqlizer(sqlizer)) } func TestDebugSqlizerErrors(t *testing.T) { errorMsg := DebugSqlizer(Expr("x = ?", 1, 2)) // Not enough placeholders assert.True(t, strings.HasPrefix(errorMsg, "[DebugSqlizer error: ")) errorMsg = DebugSqlizer(Expr("x = ? AND y = ?", 1)) // Too many placeholders assert.True(t, strings.HasPrefix(errorMsg, "[DebugSqlizer error: ")) errorMsg = DebugSqlizer(Lt{"x": nil}) // Cannot use nil values with Lt assert.True(t, strings.HasPrefix(errorMsg, "[ToSql error: ")) } ================================================ FILE: statement.go ================================================ package squirrel import "github.com/lann/builder" // StatementBuilderType is the type of StatementBuilder. type StatementBuilderType builder.Builder // Select returns a SelectBuilder for this StatementBuilderType. func (b StatementBuilderType) Select(columns ...string) SelectBuilder { return SelectBuilder(b).Columns(columns...) } // Insert returns a InsertBuilder for this StatementBuilderType. func (b StatementBuilderType) Insert(into string) InsertBuilder { return InsertBuilder(b).Into(into) } // Replace returns a InsertBuilder for this StatementBuilderType with the // statement keyword set to "REPLACE". func (b StatementBuilderType) Replace(into string) InsertBuilder { return InsertBuilder(b).statementKeyword("REPLACE").Into(into) } // Update returns a UpdateBuilder for this StatementBuilderType. func (b StatementBuilderType) Update(table string) UpdateBuilder { return UpdateBuilder(b).Table(table) } // Delete returns a DeleteBuilder for this StatementBuilderType. func (b StatementBuilderType) Delete(from string) DeleteBuilder { return DeleteBuilder(b).From(from) } // PlaceholderFormat sets the PlaceholderFormat field for any child builders. func (b StatementBuilderType) PlaceholderFormat(f PlaceholderFormat) StatementBuilderType { return builder.Set(b, "PlaceholderFormat", f).(StatementBuilderType) } // RunWith sets the RunWith field for any child builders. func (b StatementBuilderType) RunWith(runner BaseRunner) StatementBuilderType { return setRunWith(b, runner).(StatementBuilderType) } // Where adds WHERE expressions to the query. // // See SelectBuilder.Where for more information. func (b StatementBuilderType) Where(pred interface{}, args ...interface{}) StatementBuilderType { return builder.Append(b, "WhereParts", newWherePart(pred, args...)).(StatementBuilderType) } // StatementBuilder is a parent builder for other builders, e.g. SelectBuilder. var StatementBuilder = StatementBuilderType(builder.EmptyBuilder).PlaceholderFormat(Question) // Select returns a new SelectBuilder, optionally setting some result columns. // // See SelectBuilder.Columns. func Select(columns ...string) SelectBuilder { return StatementBuilder.Select(columns...) } // Insert returns a new InsertBuilder with the given table name. // // See InsertBuilder.Into. func Insert(into string) InsertBuilder { return StatementBuilder.Insert(into) } // Replace returns a new InsertBuilder with the statement keyword set to // "REPLACE" and with the given table name. // // See InsertBuilder.Into. func Replace(into string) InsertBuilder { return StatementBuilder.Replace(into) } // Update returns a new UpdateBuilder with the given table name. // // See UpdateBuilder.Table. func Update(table string) UpdateBuilder { return StatementBuilder.Update(table) } // Delete returns a new DeleteBuilder with the given table name. // // See DeleteBuilder.Table. func Delete(from string) DeleteBuilder { return StatementBuilder.Delete(from) } // Case returns a new CaseBuilder // "what" represents case value func Case(what ...interface{}) CaseBuilder { b := CaseBuilder(builder.EmptyBuilder) switch len(what) { case 0: case 1: b = b.what(what[0]) default: b = b.what(newPart(what[0], what[1:]...)) } return b } ================================================ FILE: statement_test.go ================================================ package squirrel import ( "database/sql" "testing" "github.com/lann/builder" "github.com/stretchr/testify/assert" ) func TestStatementBuilder(t *testing.T) { db := &DBStub{} sb := StatementBuilder.RunWith(db) sb.Select("test").Exec() assert.Equal(t, "SELECT test", db.LastExecSql) } func TestStatementBuilderPlaceholderFormat(t *testing.T) { db := &DBStub{} sb := StatementBuilder.RunWith(db).PlaceholderFormat(Dollar) sb.Select("test").Where("x = ?").Exec() assert.Equal(t, "SELECT test WHERE x = $1", db.LastExecSql) } func TestRunWithDB(t *testing.T) { db := &sql.DB{} assert.NotPanics(t, func() { builder.GetStruct(Select().RunWith(db)) builder.GetStruct(Insert("t").RunWith(db)) builder.GetStruct(Update("t").RunWith(db)) builder.GetStruct(Delete("t").RunWith(db)) }, "RunWith(*sql.DB) should not panic") } func TestRunWithTx(t *testing.T) { tx := &sql.Tx{} assert.NotPanics(t, func() { builder.GetStruct(Select().RunWith(tx)) builder.GetStruct(Insert("t").RunWith(tx)) builder.GetStruct(Update("t").RunWith(tx)) builder.GetStruct(Delete("t").RunWith(tx)) }, "RunWith(*sql.Tx) should not panic") } type fakeBaseRunner struct{} func (fakeBaseRunner) Exec(query string, args ...interface{}) (sql.Result, error) { return nil, nil } func (fakeBaseRunner) Query(query string, args ...interface{}) (*sql.Rows, error) { return nil, nil } func TestRunWithBaseRunner(t *testing.T) { sb := StatementBuilder.RunWith(fakeBaseRunner{}) _, err := sb.Select("test").Exec() assert.NoError(t, err) } func TestRunWithBaseRunnerQueryRowError(t *testing.T) { sb := StatementBuilder.RunWith(fakeBaseRunner{}) assert.Error(t, RunnerNotQueryRunner, sb.Select("test").QueryRow().Scan(nil)) } func TestStatementBuilderWhere(t *testing.T) { sb := StatementBuilder.Where("x = ?", 1) sql, args, err := sb.Select("test").Where("y = ?", 2).ToSql() assert.NoError(t, err) expectedSql := "SELECT test WHERE x = ? AND y = ?" assert.Equal(t, expectedSql, sql) expectedArgs := []interface{}{1, 2} assert.Equal(t, expectedArgs, args) } ================================================ FILE: stmtcacher.go ================================================ package squirrel import ( "database/sql" "fmt" "sync" ) // Prepareer is the interface that wraps the Prepare method. // // Prepare executes the given query as implemented by database/sql.Prepare. type Preparer interface { Prepare(query string) (*sql.Stmt, error) } // DBProxy groups the Execer, Queryer, QueryRower, and Preparer interfaces. type DBProxy interface { Execer Queryer QueryRower Preparer } // NOTE: NewStmtCache is defined in stmtcacher_ctx.go (Go >= 1.8) or stmtcacher_noctx.go (Go < 1.8). // StmtCache wraps and delegates down to a Preparer type // // It also automatically prepares all statements sent to the underlying Preparer calls // for Exec, Query and QueryRow and caches the returns *sql.Stmt using the provided // query as the key. So that it can be automatically re-used. type StmtCache struct { prep Preparer cache map[string]*sql.Stmt mu sync.Mutex } // Prepare delegates down to the underlying Preparer and caches the result // using the provided query as a key func (sc *StmtCache) Prepare(query string) (*sql.Stmt, error) { sc.mu.Lock() defer sc.mu.Unlock() stmt, ok := sc.cache[query] if ok { return stmt, nil } stmt, err := sc.prep.Prepare(query) if err == nil { sc.cache[query] = stmt } return stmt, err } // Exec delegates down to the underlying Preparer using a prepared statement func (sc *StmtCache) Exec(query string, args ...interface{}) (res sql.Result, err error) { stmt, err := sc.Prepare(query) if err != nil { return } return stmt.Exec(args...) } // Query delegates down to the underlying Preparer using a prepared statement func (sc *StmtCache) Query(query string, args ...interface{}) (rows *sql.Rows, err error) { stmt, err := sc.Prepare(query) if err != nil { return } return stmt.Query(args...) } // QueryRow delegates down to the underlying Preparer using a prepared statement func (sc *StmtCache) QueryRow(query string, args ...interface{}) RowScanner { stmt, err := sc.Prepare(query) if err != nil { return &Row{err: err} } return stmt.QueryRow(args...) } // Clear removes and closes all the currently cached prepared statements func (sc *StmtCache) Clear() (err error) { sc.mu.Lock() defer sc.mu.Unlock() for key, stmt := range sc.cache { delete(sc.cache, key) if stmt == nil { continue } if cerr := stmt.Close(); cerr != nil { err = cerr } } if err != nil { return fmt.Errorf("one or more Stmt.Close failed; last error: %v", err) } return } type DBProxyBeginner interface { DBProxy Begin() (*sql.Tx, error) } type stmtCacheProxy struct { DBProxy db *sql.DB } func NewStmtCacheProxy(db *sql.DB) DBProxyBeginner { return &stmtCacheProxy{DBProxy: NewStmtCache(db), db: db} } func (sp *stmtCacheProxy) Begin() (*sql.Tx, error) { return sp.db.Begin() } ================================================ FILE: stmtcacher_ctx.go ================================================ // +build go1.8 package squirrel import ( "context" "database/sql" ) // PrepareerContext is the interface that wraps the Prepare and PrepareContext methods. // // Prepare executes the given query as implemented by database/sql.Prepare. // PrepareContext executes the given query as implemented by database/sql.PrepareContext. type PreparerContext interface { Preparer PrepareContext(ctx context.Context, query string) (*sql.Stmt, error) } // DBProxyContext groups the Execer, Queryer, QueryRower and PreparerContext interfaces. type DBProxyContext interface { Execer Queryer QueryRower PreparerContext } // NewStmtCache returns a *StmtCache wrapping a PreparerContext that caches Prepared Stmts. // // Stmts are cached based on the string value of their queries. func NewStmtCache(prep PreparerContext) *StmtCache { return &StmtCache{prep: prep, cache: make(map[string]*sql.Stmt)} } // NewStmtCacher is deprecated // // Use NewStmtCache instead func NewStmtCacher(prep PreparerContext) DBProxyContext { return NewStmtCache(prep) } // PrepareContext delegates down to the underlying PreparerContext and caches the result // using the provided query as a key func (sc *StmtCache) PrepareContext(ctx context.Context, query string) (*sql.Stmt, error) { ctxPrep, ok := sc.prep.(PreparerContext) if !ok { return nil, NoContextSupport } sc.mu.Lock() defer sc.mu.Unlock() stmt, ok := sc.cache[query] if ok { return stmt, nil } stmt, err := ctxPrep.PrepareContext(ctx, query) if err == nil { sc.cache[query] = stmt } return stmt, err } // ExecContext delegates down to the underlying PreparerContext using a prepared statement func (sc *StmtCache) ExecContext(ctx context.Context, query string, args ...interface{}) (res sql.Result, err error) { stmt, err := sc.PrepareContext(ctx, query) if err != nil { return } return stmt.ExecContext(ctx, args...) } // QueryContext delegates down to the underlying PreparerContext using a prepared statement func (sc *StmtCache) QueryContext(ctx context.Context, query string, args ...interface{}) (rows *sql.Rows, err error) { stmt, err := sc.PrepareContext(ctx, query) if err != nil { return } return stmt.QueryContext(ctx, args...) } // QueryRowContext delegates down to the underlying PreparerContext using a prepared statement func (sc *StmtCache) QueryRowContext(ctx context.Context, query string, args ...interface{}) RowScanner { stmt, err := sc.PrepareContext(ctx, query) if err != nil { return &Row{err: err} } return stmt.QueryRowContext(ctx, args...) } ================================================ FILE: stmtcacher_ctx_test.go ================================================ // +build go1.8 package squirrel import ( "testing" "github.com/stretchr/testify/assert" ) func TestStmtCacherPrepareContext(t *testing.T) { db := &DBStub{} sc := NewStmtCache(db) query := "SELECT 1" sc.PrepareContext(ctx, query) assert.Equal(t, query, db.LastPrepareSql) sc.PrepareContext(ctx, query) assert.Equal(t, 1, db.PrepareCount, "expected 1 Prepare, got %d", db.PrepareCount) } ================================================ FILE: stmtcacher_noctx.go ================================================ // +build !go1.8 package squirrel import ( "database/sql" ) // NewStmtCacher returns a DBProxy wrapping prep that caches Prepared Stmts. // // Stmts are cached based on the string value of their queries. func NewStmtCache(prep Preparer) *StmtCache { return &StmtCacher{prep: prep, cache: make(map[string]*sql.Stmt)} } // NewStmtCacher is deprecated // // Use NewStmtCache instead func NewStmtCacher(prep Preparer) DBProxy { return NewStmtCache(prep) } ================================================ FILE: stmtcacher_test.go ================================================ package squirrel import ( "testing" "github.com/stretchr/testify/assert" ) func TestStmtCachePrepare(t *testing.T) { db := &DBStub{} sc := NewStmtCache(db) query := "SELECT 1" sc.Prepare(query) assert.Equal(t, query, db.LastPrepareSql) sc.Prepare(query) assert.Equal(t, 1, db.PrepareCount, "expected 1 Prepare, got %d", db.PrepareCount) // clear statement cache assert.Nil(t, sc.Clear()) // should prepare the query again sc.Prepare(query) assert.Equal(t, 2, db.PrepareCount, "expected 2 Prepare, got %d", db.PrepareCount) } ================================================ FILE: update.go ================================================ package squirrel import ( "bytes" "database/sql" "fmt" "sort" "strings" "github.com/lann/builder" ) type updateData struct { PlaceholderFormat PlaceholderFormat RunWith BaseRunner Prefixes []Sqlizer Table string SetClauses []setClause From Sqlizer WhereParts []Sqlizer OrderBys []string Limit string Offset string Suffixes []Sqlizer } type setClause struct { column string value interface{} } func (d *updateData) Exec() (sql.Result, error) { if d.RunWith == nil { return nil, RunnerNotSet } return ExecWith(d.RunWith, d) } func (d *updateData) Query() (*sql.Rows, error) { if d.RunWith == nil { return nil, RunnerNotSet } return QueryWith(d.RunWith, d) } func (d *updateData) 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 *updateData) ToSql() (sqlStr string, args []interface{}, err error) { if len(d.Table) == 0 { err = fmt.Errorf("update statements must specify a table") return } if len(d.SetClauses) == 0 { err = fmt.Errorf("update statements must have at least one Set clause") return } sql := &bytes.Buffer{} if len(d.Prefixes) > 0 { args, err = appendToSql(d.Prefixes, sql, " ", args) if err != nil { return } sql.WriteString(" ") } sql.WriteString("UPDATE ") sql.WriteString(d.Table) sql.WriteString(" SET ") setSqls := make([]string, len(d.SetClauses)) for i, setClause := range d.SetClauses { var valSql string if vs, ok := setClause.value.(Sqlizer); ok { vsql, vargs, err := vs.ToSql() if err != nil { return "", nil, err } if _, ok := vs.(SelectBuilder); ok { valSql = fmt.Sprintf("(%s)", vsql) } else { valSql = vsql } args = append(args, vargs...) } else { valSql = "?" args = append(args, setClause.value) } setSqls[i] = fmt.Sprintf("%s = %s", setClause.column, valSql) } sql.WriteString(strings.Join(setSqls, ", ")) if d.From != nil { sql.WriteString(" FROM ") args, err = appendToSql([]Sqlizer{d.From}, 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.OrderBys) > 0 { sql.WriteString(" ORDER BY ") sql.WriteString(strings.Join(d.OrderBys, ", ")) } 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, err = d.PlaceholderFormat.ReplacePlaceholders(sql.String()) return } // Builder // UpdateBuilder builds SQL UPDATE statements. type UpdateBuilder builder.Builder func init() { builder.Register(UpdateBuilder{}, updateData{}) } // Format methods // PlaceholderFormat sets PlaceholderFormat (e.g. Question or Dollar) for the // query. func (b UpdateBuilder) PlaceholderFormat(f PlaceholderFormat) UpdateBuilder { return builder.Set(b, "PlaceholderFormat", f).(UpdateBuilder) } // Runner methods // RunWith sets a Runner (like database/sql.DB) to be used with e.g. Exec. func (b UpdateBuilder) RunWith(runner BaseRunner) UpdateBuilder { return setRunWith(b, runner).(UpdateBuilder) } // Exec builds and Execs the query with the Runner set by RunWith. func (b UpdateBuilder) Exec() (sql.Result, error) { data := builder.GetStruct(b).(updateData) return data.Exec() } func (b UpdateBuilder) Query() (*sql.Rows, error) { data := builder.GetStruct(b).(updateData) return data.Query() } func (b UpdateBuilder) QueryRow() RowScanner { data := builder.GetStruct(b).(updateData) return data.QueryRow() } func (b UpdateBuilder) Scan(dest ...interface{}) error { return b.QueryRow().Scan(dest...) } // SQL methods // ToSql builds the query into a SQL string and bound args. func (b UpdateBuilder) ToSql() (string, []interface{}, error) { data := builder.GetStruct(b).(updateData) return data.ToSql() } // MustSql builds the query into a SQL string and bound args. // It panics if there are any errors. func (b UpdateBuilder) MustSql() (string, []interface{}) { sql, args, err := b.ToSql() if err != nil { panic(err) } return sql, args } // Prefix adds an expression to the beginning of the query func (b UpdateBuilder) Prefix(sql string, args ...interface{}) UpdateBuilder { return b.PrefixExpr(Expr(sql, args...)) } // PrefixExpr adds an expression to the very beginning of the query func (b UpdateBuilder) PrefixExpr(expr Sqlizer) UpdateBuilder { return builder.Append(b, "Prefixes", expr).(UpdateBuilder) } // Table sets the table to be updated. func (b UpdateBuilder) Table(table string) UpdateBuilder { return builder.Set(b, "Table", table).(UpdateBuilder) } // Set adds SET clauses to the query. func (b UpdateBuilder) Set(column string, value interface{}) UpdateBuilder { return builder.Append(b, "SetClauses", setClause{column: column, value: value}).(UpdateBuilder) } // SetMap is a convenience method which calls .Set for each key/value pair in clauses. func (b UpdateBuilder) SetMap(clauses map[string]interface{}) UpdateBuilder { keys := make([]string, len(clauses)) i := 0 for key := range clauses { keys[i] = key i++ } sort.Strings(keys) for _, key := range keys { val, _ := clauses[key] b = b.Set(key, val) } return b } // From adds FROM clause to the query // FROM is valid construct in postgresql only. func (b UpdateBuilder) From(from string) UpdateBuilder { return builder.Set(b, "From", newPart(from)).(UpdateBuilder) } // FromSelect sets a subquery into the FROM clause of the query. func (b UpdateBuilder) FromSelect(from SelectBuilder, alias string) UpdateBuilder { // Prevent misnumbered parameters in nested selects (#183). from = from.PlaceholderFormat(Question) return builder.Set(b, "From", Alias(from, alias)).(UpdateBuilder) } // Where adds WHERE expressions to the query. // // See SelectBuilder.Where for more information. func (b UpdateBuilder) Where(pred interface{}, args ...interface{}) UpdateBuilder { return builder.Append(b, "WhereParts", newWherePart(pred, args...)).(UpdateBuilder) } // OrderBy adds ORDER BY expressions to the query. func (b UpdateBuilder) OrderBy(orderBys ...string) UpdateBuilder { return builder.Extend(b, "OrderBys", orderBys).(UpdateBuilder) } // Limit sets a LIMIT clause on the query. func (b UpdateBuilder) Limit(limit uint64) UpdateBuilder { return builder.Set(b, "Limit", fmt.Sprintf("%d", limit)).(UpdateBuilder) } // Offset sets a OFFSET clause on the query. func (b UpdateBuilder) Offset(offset uint64) UpdateBuilder { return builder.Set(b, "Offset", fmt.Sprintf("%d", offset)).(UpdateBuilder) } // Suffix adds an expression to the end of the query func (b UpdateBuilder) Suffix(sql string, args ...interface{}) UpdateBuilder { return b.SuffixExpr(Expr(sql, args...)) } // SuffixExpr adds an expression to the end of the query func (b UpdateBuilder) SuffixExpr(expr Sqlizer) UpdateBuilder { return builder.Append(b, "Suffixes", expr).(UpdateBuilder) } ================================================ FILE: update_ctx.go ================================================ // +build go1.8 package squirrel import ( "context" "database/sql" "github.com/lann/builder" ) func (d *updateData) ExecContext(ctx context.Context) (sql.Result, error) { if d.RunWith == nil { return nil, RunnerNotSet } ctxRunner, ok := d.RunWith.(ExecerContext) if !ok { return nil, NoContextSupport } return ExecContextWith(ctx, ctxRunner, d) } func (d *updateData) QueryContext(ctx context.Context) (*sql.Rows, error) { if d.RunWith == nil { return nil, RunnerNotSet } ctxRunner, ok := d.RunWith.(QueryerContext) if !ok { return nil, NoContextSupport } return QueryContextWith(ctx, ctxRunner, d) } func (d *updateData) QueryRowContext(ctx context.Context) RowScanner { if d.RunWith == nil { return &Row{err: RunnerNotSet} } queryRower, ok := d.RunWith.(QueryRowerContext) if !ok { if _, ok := d.RunWith.(QueryerContext); !ok { return &Row{err: RunnerNotQueryRunner} } return &Row{err: NoContextSupport} } return QueryRowContextWith(ctx, queryRower, d) } // ExecContext builds and ExecContexts the query with the Runner set by RunWith. func (b UpdateBuilder) ExecContext(ctx context.Context) (sql.Result, error) { data := builder.GetStruct(b).(updateData) return data.ExecContext(ctx) } // QueryContext builds and QueryContexts the query with the Runner set by RunWith. func (b UpdateBuilder) QueryContext(ctx context.Context) (*sql.Rows, error) { data := builder.GetStruct(b).(updateData) return data.QueryContext(ctx) } // QueryRowContext builds and QueryRowContexts the query with the Runner set by RunWith. func (b UpdateBuilder) QueryRowContext(ctx context.Context) RowScanner { data := builder.GetStruct(b).(updateData) return data.QueryRowContext(ctx) } // ScanContext is a shortcut for QueryRowContext().Scan. func (b UpdateBuilder) ScanContext(ctx context.Context, dest ...interface{}) error { return b.QueryRowContext(ctx).Scan(dest...) } ================================================ FILE: update_ctx_test.go ================================================ // +build go1.8 package squirrel import ( "testing" "github.com/stretchr/testify/assert" ) func TestUpdateBuilderContextRunners(t *testing.T) { db := &DBStub{} b := Update("test").Set("x", 1).RunWith(db) expectedSql := "UPDATE test SET x = ?" b.ExecContext(ctx) assert.Equal(t, expectedSql, db.LastExecSql) b.QueryContext(ctx) assert.Equal(t, expectedSql, db.LastQuerySql) b.QueryRowContext(ctx) assert.Equal(t, expectedSql, db.LastQueryRowSql) err := b.ScanContext(ctx) assert.NoError(t, err) } func TestUpdateBuilderContextNoRunner(t *testing.T) { b := Update("test").Set("x", 1) _, err := b.ExecContext(ctx) assert.Equal(t, RunnerNotSet, err) _, err = b.QueryContext(ctx) assert.Equal(t, RunnerNotSet, err) err = b.ScanContext(ctx) assert.Equal(t, RunnerNotSet, err) } ================================================ FILE: update_test.go ================================================ package squirrel import ( "testing" "github.com/stretchr/testify/assert" ) func TestUpdateBuilderToSql(t *testing.T) { b := Update(""). Prefix("WITH prefix AS ?", 0). Table("a"). Set("b", Expr("? + 1", 1)). SetMap(Eq{"c": 2}). Set("c1", Case("status").When("1", "2").When("2", "1")). Set("c2", Case().When("a = 2", Expr("?", "foo")).When("a = 3", Expr("?", "bar"))). Set("c3", Select("a").From("b")). Where("d = ?", 3). OrderBy("e"). Limit(4). Offset(5). Suffix("RETURNING ?", 6) sql, args, err := b.ToSql() assert.NoError(t, err) expectedSql := "WITH prefix AS ? " + "UPDATE a SET b = ? + 1, c = ?, " + "c1 = CASE status WHEN 1 THEN 2 WHEN 2 THEN 1 END, " + "c2 = CASE WHEN a = 2 THEN ? WHEN a = 3 THEN ? END, " + "c3 = (SELECT a FROM b) " + "WHERE d = ? " + "ORDER BY e LIMIT 4 OFFSET 5 " + "RETURNING ?" assert.Equal(t, expectedSql, sql) expectedArgs := []interface{}{0, 1, 2, "foo", "bar", 3, 6} assert.Equal(t, expectedArgs, args) } func TestUpdateBuilderToSqlErr(t *testing.T) { _, _, err := Update("").Set("x", 1).ToSql() assert.Error(t, err) _, _, err = Update("x").ToSql() assert.Error(t, err) } func TestUpdateBuilderMustSql(t *testing.T) { defer func() { if r := recover(); r == nil { t.Errorf("TestUpdateBuilderMustSql should have panicked!") } }() Update("").MustSql() } func TestUpdateBuilderPlaceholders(t *testing.T) { b := Update("test").SetMap(Eq{"x": 1, "y": 2}) sql, _, _ := b.PlaceholderFormat(Question).ToSql() assert.Equal(t, "UPDATE test SET x = ?, y = ?", sql) sql, _, _ = b.PlaceholderFormat(Dollar).ToSql() assert.Equal(t, "UPDATE test SET x = $1, y = $2", sql) } func TestUpdateBuilderRunners(t *testing.T) { db := &DBStub{} b := Update("test").Set("x", 1).RunWith(db) expectedSql := "UPDATE test SET x = ?" b.Exec() assert.Equal(t, expectedSql, db.LastExecSql) } func TestUpdateBuilderNoRunner(t *testing.T) { b := Update("test").Set("x", 1) _, err := b.Exec() assert.Equal(t, RunnerNotSet, err) } func TestUpdateBuilderFrom(t *testing.T) { sql, _, err := Update("employees").Set("sales_count", 100).From("accounts").Where("accounts.name = ?", "ACME").ToSql() assert.NoError(t, err) assert.Equal(t, "UPDATE employees SET sales_count = ? FROM accounts WHERE accounts.name = ?", sql) } func TestUpdateBuilderFromSelect(t *testing.T) { sql, _, err := Update("employees"). Set("sales_count", 100). FromSelect(Select("id"). From("accounts"). Where("accounts.name = ?", "ACME"), "subquery"). Where("employees.account_id = subquery.id").ToSql() assert.NoError(t, err) expectedSql := "UPDATE employees " + "SET sales_count = ? " + "FROM (SELECT id FROM accounts WHERE accounts.name = ?) AS subquery " + "WHERE employees.account_id = subquery.id" assert.Equal(t, expectedSql, sql) } ================================================ FILE: where.go ================================================ package squirrel import ( "fmt" ) type wherePart part func newWherePart(pred interface{}, args ...interface{}) Sqlizer { return &wherePart{pred: pred, args: args} } func (p wherePart) ToSql() (sql string, args []interface{}, err error) { switch pred := p.pred.(type) { case nil: // no-op case rawSqlizer: return pred.toSqlRaw() case Sqlizer: return pred.ToSql() case map[string]interface{}: return Eq(pred).ToSql() case string: sql = pred args = p.args default: err = fmt.Errorf("expected string-keyed map or string, not %T", pred) } return } ================================================ FILE: where_test.go ================================================ package squirrel import ( "testing" "bytes" "github.com/stretchr/testify/assert" ) func TestWherePartsAppendToSql(t *testing.T) { parts := []Sqlizer{ newWherePart("x = ?", 1), newWherePart(nil), newWherePart(Eq{"y": 2}), } sql := &bytes.Buffer{} args, _ := appendToSql(parts, sql, " AND ", []interface{}{}) assert.Equal(t, "x = ? AND y = ?", sql.String()) assert.Equal(t, []interface{}{1, 2}, args) } func TestWherePartsAppendToSqlErr(t *testing.T) { parts := []Sqlizer{newWherePart(1)} _, err := appendToSql(parts, &bytes.Buffer{}, "", []interface{}{}) assert.Error(t, err) } func TestWherePartNil(t *testing.T) { sql, _, _ := newWherePart(nil).ToSql() assert.Equal(t, "", sql) } func TestWherePartErr(t *testing.T) { _, _, err := newWherePart(1).ToSql() assert.Error(t, err) } func TestWherePartString(t *testing.T) { sql, args, _ := newWherePart("x = ?", 1).ToSql() assert.Equal(t, "x = ?", sql) assert.Equal(t, []interface{}{1}, args) } func TestWherePartMap(t *testing.T) { test := func(pred interface{}) { sql, _, _ := newWherePart(pred).ToSql() expect := []string{"x = ? AND y = ?", "y = ? AND x = ?"} if sql != expect[0] && sql != expect[1] { t.Errorf("expected one of %#v, got %#v", expect, sql) } } m := map[string]interface{}{"x": 1, "y": 2} test(m) test(Eq(m)) }