mirror of
https://github.com/mattermost/mattermost.git
synced 2025-02-25 18:55:24 -06:00
* add query builder to facilitate dynamic SQL construction * leverage query builder to refactor user store This is partially setup work for MM-13120, but merged to master to avoid further conflicts. * fix unrelated unit test breakage * documentation tweaks * Apply suggestions from code review Co-Authored-By: lieut-data <jesse.hallam@gmail.com> * prefer comma separated case options to fallthrough * vendor github.com/Masterminds/squirrel and deps * switch to using github.com/Masterminds/squirrel * rm querybuilder
173 lines
4.6 KiB
Go
173 lines
4.6 KiB
Go
// Package squirrel provides a fluent SQL generator.
|
|
//
|
|
// See https://github.com/Masterminds/squirrel for examples.
|
|
package squirrel
|
|
|
|
import (
|
|
"bytes"
|
|
"context"
|
|
"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
|
|
}
|
|
|
|
type stdsql interface {
|
|
Query(string, ...interface{}) (*sql.Rows, error)
|
|
QueryContext(context.Context, string, ...interface{}) (*sql.Rows, error)
|
|
QueryRow(string, ...interface{}) *sql.Row
|
|
QueryRowContext(context.Context, string, ...interface{}) *sql.Row
|
|
Exec(string, ...interface{}) (sql.Result, error)
|
|
ExecContext(context.Context, 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{}, baseRunner BaseRunner) interface{} {
|
|
var runner Runner
|
|
switch r := baseRunner.(type) {
|
|
case Runner:
|
|
runner = r
|
|
case stdsql:
|
|
runner = &stdsqlRunner{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)
|
|
}
|
|
|
|
// TODO: dedupe this with placeholder.go
|
|
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 {
|
|
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])
|
|
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))
|
|
}
|
|
buf.WriteString(sql)
|
|
return buf.String()
|
|
}
|