115 lines
2.5 KiB
Go
115 lines
2.5 KiB
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
|
||
|
}
|