// 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() }