Refactor clause Writer

This commit is contained in:
Jinzhu 2020-03-09 17:07:00 +08:00
parent 3aa1891068
commit 504f42760a
20 changed files with 117 additions and 108 deletions

View File

@ -12,13 +12,16 @@ type ClauseBuilder interface {
Build(Clause, Builder) Build(Clause, Builder)
} }
type Writer interface {
WriteByte(byte) error
WriteString(string) (int, error)
}
// Builder builder interface // Builder builder interface
type Builder interface { type Builder interface {
WriteByte(byte) error Writer
Write(sql ...string) error
WriteQuoted(field interface{}) error WriteQuoted(field interface{}) error
AddVar(vars ...interface{}) string AddVar(Writer, ...interface{})
Quote(field interface{}) string
} }
// Clause // Clause

View File

@ -9,11 +9,11 @@ func (d Delete) Name() string {
} }
func (d Delete) Build(builder Builder) { func (d Delete) Build(builder Builder) {
builder.Write("DELETE") builder.WriteString("DELETE")
if d.Modifier != "" { if d.Modifier != "" {
builder.WriteByte(' ') builder.WriteByte(' ')
builder.Write(d.Modifier) builder.WriteString(d.Modifier)
} }
} }

View File

@ -1,9 +1,5 @@
package clause package clause
import (
"strings"
)
// Expression expression interface // Expression expression interface
type Expression interface { type Expression interface {
Build(builder Builder) Build(builder Builder)
@ -22,11 +18,15 @@ type Expr struct {
// Build build raw expression // Build build raw expression
func (expr Expr) Build(builder Builder) { func (expr Expr) Build(builder Builder) {
sql := expr.SQL var idx int
for _, v := range expr.Vars { for _, v := range []byte(expr.SQL) {
sql = strings.Replace(sql, "?", builder.AddVar(v), 1) if v == '?' {
builder.AddVar(builder, expr.Vars[idx])
idx++
} else {
builder.WriteByte(v)
}
} }
builder.Write(sql)
} }
// IN Whether a value is within a set of values // IN Whether a value is within a set of values
@ -40,11 +40,14 @@ func (in IN) Build(builder Builder) {
switch len(in.Values) { switch len(in.Values) {
case 0: case 0:
builder.Write(" IN (NULL)") builder.WriteString(" IN (NULL)")
case 1: case 1:
builder.Write(" = ", builder.AddVar(in.Values...)) builder.WriteString(" = ")
builder.AddVar(builder, in.Values...)
default: default:
builder.Write(" IN (", builder.AddVar(in.Values...), ")") builder.WriteString(" IN (")
builder.AddVar(builder, in.Values...)
builder.WriteByte(')')
} }
} }
@ -52,9 +55,12 @@ func (in IN) NegationBuild(builder Builder) {
switch len(in.Values) { switch len(in.Values) {
case 0: case 0:
case 1: case 1:
builder.Write(" <> ", builder.AddVar(in.Values...)) builder.WriteString(" <> ")
builder.AddVar(builder, in.Values...)
default: default:
builder.Write(" NOT IN (", builder.AddVar(in.Values...), ")") builder.WriteString(" NOT IN (")
builder.AddVar(builder, in.Values...)
builder.WriteByte(')')
} }
} }
@ -68,9 +74,10 @@ func (eq Eq) Build(builder Builder) {
builder.WriteQuoted(eq.Column) builder.WriteQuoted(eq.Column)
if eq.Value == nil { if eq.Value == nil {
builder.Write(" IS NULL") builder.WriteString(" IS NULL")
} else { } else {
builder.Write(" = ", builder.AddVar(eq.Value)) builder.WriteString(" = ")
builder.AddVar(builder, eq.Value)
} }
} }
@ -85,9 +92,10 @@ func (neq Neq) Build(builder Builder) {
builder.WriteQuoted(neq.Column) builder.WriteQuoted(neq.Column)
if neq.Value == nil { if neq.Value == nil {
builder.Write(" IS NOT NULL") builder.WriteString(" IS NOT NULL")
} else { } else {
builder.Write(" <> ", builder.AddVar(neq.Value)) builder.WriteString(" <> ")
builder.AddVar(builder, neq.Value)
} }
} }
@ -100,7 +108,8 @@ type Gt Eq
func (gt Gt) Build(builder Builder) { func (gt Gt) Build(builder Builder) {
builder.WriteQuoted(gt.Column) builder.WriteQuoted(gt.Column)
builder.Write(" > ", builder.AddVar(gt.Value)) builder.WriteString(" > ")
builder.AddVar(builder, gt.Value)
} }
func (gt Gt) NegationBuild(builder Builder) { func (gt Gt) NegationBuild(builder Builder) {
@ -112,7 +121,8 @@ type Gte Eq
func (gte Gte) Build(builder Builder) { func (gte Gte) Build(builder Builder) {
builder.WriteQuoted(gte.Column) builder.WriteQuoted(gte.Column)
builder.Write(" >= ", builder.AddVar(gte.Value)) builder.WriteString(" >= ")
builder.AddVar(builder, gte.Value)
} }
func (gte Gte) NegationBuild(builder Builder) { func (gte Gte) NegationBuild(builder Builder) {
@ -124,7 +134,8 @@ type Lt Eq
func (lt Lt) Build(builder Builder) { func (lt Lt) Build(builder Builder) {
builder.WriteQuoted(lt.Column) builder.WriteQuoted(lt.Column)
builder.Write(" < ", builder.AddVar(lt.Value)) builder.WriteString(" < ")
builder.AddVar(builder, lt.Value)
} }
func (lt Lt) NegationBuild(builder Builder) { func (lt Lt) NegationBuild(builder Builder) {
@ -136,7 +147,8 @@ type Lte Eq
func (lte Lte) Build(builder Builder) { func (lte Lte) Build(builder Builder) {
builder.WriteQuoted(lte.Column) builder.WriteQuoted(lte.Column)
builder.Write(" <= ", builder.AddVar(lte.Value)) builder.WriteString(" <= ")
builder.AddVar(builder, lte.Value)
} }
func (lte Lte) NegationBuild(builder Builder) { func (lte Lte) NegationBuild(builder Builder) {
@ -148,12 +160,14 @@ type Like Eq
func (like Like) Build(builder Builder) { func (like Like) Build(builder Builder) {
builder.WriteQuoted(like.Column) builder.WriteQuoted(like.Column)
builder.Write(" LIKE ", builder.AddVar(like.Value)) builder.WriteString(" LIKE ")
builder.AddVar(builder, like.Value)
} }
func (like Like) NegationBuild(builder Builder) { func (like Like) NegationBuild(builder Builder) {
builder.WriteQuoted(like.Column) builder.WriteQuoted(like.Column)
builder.Write(" NOT LIKE ", builder.AddVar(like.Value)) builder.WriteString(" NOT LIKE ")
builder.AddVar(builder, like.Value)
} }
// Map // Map

View File

@ -50,18 +50,18 @@ func (from From) Build(builder Builder) {
func (join Join) Build(builder Builder) { func (join Join) Build(builder Builder) {
if join.Type != "" { if join.Type != "" {
builder.Write(string(join.Type)) builder.WriteString(string(join.Type))
builder.WriteByte(' ') builder.WriteByte(' ')
} }
builder.Write("JOIN ") builder.WriteString("JOIN ")
builder.WriteQuoted(join.Table) builder.WriteQuoted(join.Table)
if len(join.ON.Exprs) > 0 { if len(join.ON.Exprs) > 0 {
builder.Write(" ON ") builder.WriteString(" ON ")
join.ON.Build(builder) join.ON.Build(builder)
} else if len(join.Using) > 0 { } else if len(join.Using) > 0 {
builder.Write(" USING (") builder.WriteString(" USING (")
for idx, c := range join.Using { for idx, c := range join.Using {
if idx > 0 { if idx > 0 {
builder.WriteByte(',') builder.WriteByte(',')

View File

@ -22,7 +22,7 @@ func (groupBy GroupBy) Build(builder Builder) {
} }
if len(groupBy.Having) > 0 { if len(groupBy.Having) > 0 {
builder.Write(" HAVING ") builder.WriteString(" HAVING ")
Where{Exprs: groupBy.Having}.Build(builder) Where{Exprs: groupBy.Having}.Build(builder)
} }
} }

View File

@ -13,11 +13,11 @@ func (insert Insert) Name() string {
// Build build insert clause // Build build insert clause
func (insert Insert) Build(builder Builder) { func (insert Insert) Build(builder Builder) {
if insert.Modifier != "" { if insert.Modifier != "" {
builder.Write(insert.Modifier) builder.WriteString(insert.Modifier)
builder.WriteByte(' ') builder.WriteByte(' ')
} }
builder.Write("INTO ") builder.WriteString("INTO ")
if insert.Table.Name == "" { if insert.Table.Name == "" {
builder.WriteQuoted(currentTable) builder.WriteQuoted(currentTable)
} else { } else {

View File

@ -16,12 +16,12 @@ func (limit Limit) Name() string {
// Build build where clause // Build build where clause
func (limit Limit) Build(builder Builder) { func (limit Limit) Build(builder Builder) {
if limit.Limit > 0 { if limit.Limit > 0 {
builder.Write("LIMIT ") builder.WriteString("LIMIT ")
builder.Write(strconv.Itoa(limit.Limit)) builder.WriteString(strconv.Itoa(limit.Limit))
if limit.Offset > 0 { if limit.Offset > 0 {
builder.Write(" OFFSET ") builder.WriteString(" OFFSET ")
builder.Write(strconv.Itoa(limit.Offset)) builder.WriteString(strconv.Itoa(limit.Offset))
} }
} }
} }

View File

@ -22,16 +22,16 @@ func (f For) Build(builder Builder) {
builder.WriteByte(' ') builder.WriteByte(' ')
} }
builder.Write("FOR ") builder.WriteString("FOR ")
builder.Write(locking.Strength) builder.WriteString(locking.Strength)
if locking.Table.Name != "" { if locking.Table.Name != "" {
builder.Write(" OF ") builder.WriteString(" OF ")
builder.WriteQuoted(locking.Table) builder.WriteQuoted(locking.Table)
} }
if locking.Options != "" { if locking.Options != "" {
builder.WriteByte(' ') builder.WriteByte(' ')
builder.Write(locking.Options) builder.WriteString(locking.Options)
} }
} }
} }

View File

@ -24,7 +24,7 @@ func (orderBy OrderBy) Build(builder Builder) {
builder.WriteQuoted(column.Column) builder.WriteQuoted(column.Column)
if column.Desc { if column.Desc {
builder.Write(" DESC") builder.WriteString(" DESC")
} }
} }
} }

View File

@ -19,7 +19,7 @@ func (set Set) Build(builder Builder) {
} }
builder.WriteQuoted(assignment.Column) builder.WriteQuoted(assignment.Column)
builder.WriteByte('=') builder.WriteByte('=')
builder.Write(builder.AddVar(assignment.Value)) builder.AddVar(builder, assignment.Value)
} }
} else { } else {
builder.WriteQuoted(PrimaryColumn) builder.WriteQuoted(PrimaryColumn)

View File

@ -13,7 +13,7 @@ func (update Update) Name() string {
// Build build update clause // Build build update clause
func (update Update) Build(builder Builder) { func (update Update) Build(builder Builder) {
if update.Modifier != "" { if update.Modifier != "" {
builder.Write(update.Modifier) builder.WriteString(update.Modifier)
builder.WriteByte(' ') builder.WriteByte(' ')
} }

View File

@ -22,7 +22,7 @@ func (values Values) Build(builder Builder) {
} }
builder.WriteByte(')') builder.WriteByte(')')
builder.Write(" VALUES ") builder.WriteString(" VALUES ")
for idx, value := range values.Values { for idx, value := range values.Values {
if idx > 0 { if idx > 0 {
@ -30,11 +30,11 @@ func (values Values) Build(builder Builder) {
} }
builder.WriteByte('(') builder.WriteByte('(')
builder.Write(builder.AddVar(value...)) builder.AddVar(builder, value...)
builder.WriteByte(')') builder.WriteByte(')')
} }
} else { } else {
builder.Write("DEFAULT VALUES") builder.WriteString("DEFAULT VALUES")
} }
} }

View File

@ -26,9 +26,9 @@ func (where Where) Build(builder Builder) {
if expr != nil { if expr != nil {
if idx > 0 { if idx > 0 {
if v, ok := expr.(OrConditions); ok && len(v.Exprs) == 1 { if v, ok := expr.(OrConditions); ok && len(v.Exprs) == 1 {
builder.Write(" OR ") builder.WriteString(" OR ")
} else { } else {
builder.Write(" AND ") builder.WriteString(" AND ")
} }
} }
@ -65,7 +65,7 @@ func (and AndConditions) Build(builder Builder) {
} }
for idx, c := range and.Exprs { for idx, c := range and.Exprs {
if idx > 0 { if idx > 0 {
builder.Write(" AND ") builder.WriteString(" AND ")
} }
c.Build(builder) c.Build(builder)
} }
@ -91,7 +91,7 @@ func (or OrConditions) Build(builder Builder) {
} }
for idx, c := range or.Exprs { for idx, c := range or.Exprs {
if idx > 0 { if idx > 0 {
builder.Write(" OR ") builder.WriteString(" OR ")
} }
c.Build(builder) c.Build(builder)
} }
@ -117,13 +117,13 @@ func (not NotConditions) Build(builder Builder) {
} }
for idx, c := range not.Exprs { for idx, c := range not.Exprs {
if idx > 0 { if idx > 0 {
builder.Write(" AND ") builder.WriteString(" AND ")
} }
if negationBuilder, ok := c.(NegationExpressionBuilder); ok { if negationBuilder, ok := c.(NegationExpressionBuilder); ok {
negationBuilder.NegationBuild(builder) negationBuilder.NegationBuild(builder)
} else { } else {
builder.Write(" NOT ") builder.WriteString(" NOT ")
c.Build(builder) c.Build(builder)
} }
} }

View File

@ -5,11 +5,11 @@ import (
"fmt" "fmt"
"regexp" "regexp"
"strconv" "strconv"
"strings"
_ "github.com/denisenkom/go-mssqldb" _ "github.com/denisenkom/go-mssqldb"
"github.com/jinzhu/gorm" "github.com/jinzhu/gorm"
"github.com/jinzhu/gorm/callbacks" "github.com/jinzhu/gorm/callbacks"
"github.com/jinzhu/gorm/clause"
"github.com/jinzhu/gorm/logger" "github.com/jinzhu/gorm/logger"
"github.com/jinzhu/gorm/migrator" "github.com/jinzhu/gorm/migrator"
"github.com/jinzhu/gorm/schema" "github.com/jinzhu/gorm/schema"
@ -42,10 +42,10 @@ func (dialector Dialector) BindVar(stmt *gorm.Statement, v interface{}) string {
return "@p" + strconv.Itoa(len(stmt.Vars)) return "@p" + strconv.Itoa(len(stmt.Vars))
} }
func (dialector Dialector) QuoteTo(builder *strings.Builder, str string) { func (dialector Dialector) QuoteTo(writer clause.Writer, str string) {
builder.WriteByte('"') writer.WriteByte('"')
builder.WriteString(str) writer.WriteString(str)
builder.WriteByte('"') writer.WriteByte('"')
} }
var numericPlaceholder = regexp.MustCompile("@p(\\d+)") var numericPlaceholder = regexp.MustCompile("@p(\\d+)")

View File

@ -4,11 +4,11 @@ import (
"database/sql" "database/sql"
"fmt" "fmt"
"math" "math"
"strings"
_ "github.com/go-sql-driver/mysql" _ "github.com/go-sql-driver/mysql"
"github.com/jinzhu/gorm" "github.com/jinzhu/gorm"
"github.com/jinzhu/gorm/callbacks" "github.com/jinzhu/gorm/callbacks"
"github.com/jinzhu/gorm/clause"
"github.com/jinzhu/gorm/logger" "github.com/jinzhu/gorm/logger"
"github.com/jinzhu/gorm/migrator" "github.com/jinzhu/gorm/migrator"
"github.com/jinzhu/gorm/schema" "github.com/jinzhu/gorm/schema"
@ -40,10 +40,10 @@ func (dialector Dialector) BindVar(stmt *gorm.Statement, v interface{}) string {
return "?" return "?"
} }
func (dialector Dialector) QuoteTo(builder *strings.Builder, str string) { func (dialector Dialector) QuoteTo(writer clause.Writer, str string) {
builder.WriteByte('`') writer.WriteByte('`')
builder.WriteString(str) writer.WriteString(str)
builder.WriteByte('`') writer.WriteByte('`')
} }
func (dialector Dialector) Explain(sql string, vars ...interface{}) string { func (dialector Dialector) Explain(sql string, vars ...interface{}) string {

View File

@ -5,10 +5,10 @@ import (
"fmt" "fmt"
"regexp" "regexp"
"strconv" "strconv"
"strings"
"github.com/jinzhu/gorm" "github.com/jinzhu/gorm"
"github.com/jinzhu/gorm/callbacks" "github.com/jinzhu/gorm/callbacks"
"github.com/jinzhu/gorm/clause"
"github.com/jinzhu/gorm/logger" "github.com/jinzhu/gorm/logger"
"github.com/jinzhu/gorm/migrator" "github.com/jinzhu/gorm/migrator"
"github.com/jinzhu/gorm/schema" "github.com/jinzhu/gorm/schema"
@ -42,10 +42,10 @@ func (dialector Dialector) BindVar(stmt *gorm.Statement, v interface{}) string {
return "$" + strconv.Itoa(len(stmt.Vars)) return "$" + strconv.Itoa(len(stmt.Vars))
} }
func (dialector Dialector) QuoteTo(builder *strings.Builder, str string) { func (dialector Dialector) QuoteTo(writer clause.Writer, str string) {
builder.WriteByte('"') writer.WriteByte('"')
builder.WriteString(str) writer.WriteString(str)
builder.WriteByte('"') writer.WriteByte('"')
} }
var numericPlaceholder = regexp.MustCompile("\\$(\\d+)") var numericPlaceholder = regexp.MustCompile("\\$(\\d+)")

View File

@ -2,10 +2,10 @@ package sqlite
import ( import (
"database/sql" "database/sql"
"strings"
"github.com/jinzhu/gorm" "github.com/jinzhu/gorm"
"github.com/jinzhu/gorm/callbacks" "github.com/jinzhu/gorm/callbacks"
"github.com/jinzhu/gorm/clause"
"github.com/jinzhu/gorm/logger" "github.com/jinzhu/gorm/logger"
"github.com/jinzhu/gorm/migrator" "github.com/jinzhu/gorm/migrator"
"github.com/jinzhu/gorm/schema" "github.com/jinzhu/gorm/schema"
@ -39,10 +39,10 @@ func (dialector Dialector) BindVar(stmt *gorm.Statement, v interface{}) string {
return "?" return "?"
} }
func (dialector Dialector) QuoteTo(builder *strings.Builder, str string) { func (dialector Dialector) QuoteTo(writer clause.Writer, str string) {
builder.WriteByte('`') writer.WriteByte('`')
builder.WriteString(str) writer.WriteString(str)
builder.WriteByte('`') writer.WriteByte('`')
} }
func (dialector Dialector) Explain(sql string, vars ...interface{}) string { func (dialector Dialector) Explain(sql string, vars ...interface{}) string {

View File

@ -3,8 +3,8 @@ package gorm
import ( import (
"context" "context"
"database/sql" "database/sql"
"strings"
"github.com/jinzhu/gorm/clause"
"github.com/jinzhu/gorm/schema" "github.com/jinzhu/gorm/schema"
) )
@ -14,7 +14,7 @@ type Dialector interface {
Migrator(db *DB) Migrator Migrator(db *DB) Migrator
DataTypeOf(*schema.Field) string DataTypeOf(*schema.Field) string
BindVar(stmt *Statement, v interface{}) string BindVar(stmt *Statement, v interface{}) string
QuoteTo(*strings.Builder, string) QuoteTo(clause.Writer, string)
Explain(sql string, vars ...interface{}) string Explain(sql string, vars ...interface{}) string
} }

View File

@ -34,7 +34,6 @@ type Statement struct {
SQL strings.Builder SQL strings.Builder
Vars []interface{} Vars []interface{}
NamedVars []sql.NamedArg NamedVars []sql.NamedArg
placeholders strings.Builder
} }
// StatementOptimizer statement optimizer interface // StatementOptimizer statement optimizer interface
@ -43,15 +42,12 @@ type StatementOptimizer interface {
} }
// Write write string // Write write string
func (stmt *Statement) Write(sql ...string) (err error) { func (stmt *Statement) WriteString(str string) (int, error) {
for _, s := range sql { return stmt.SQL.WriteString(str)
_, err = stmt.SQL.WriteString(s)
}
return
} }
// Write write string // Write write string
func (stmt *Statement) WriteByte(c byte) (err error) { func (stmt *Statement) WriteByte(c byte) error {
return stmt.SQL.WriteByte(c) return stmt.SQL.WriteByte(c)
} }
@ -62,7 +58,7 @@ func (stmt *Statement) WriteQuoted(value interface{}) error {
} }
// QuoteTo write quoted value to writer // QuoteTo write quoted value to writer
func (stmt Statement) QuoteTo(writer *strings.Builder, field interface{}) { func (stmt Statement) QuoteTo(writer clause.Writer, field interface{}) {
switch v := field.(type) { switch v := field.(type) {
case clause.Table: case clause.Table:
if v.Name == clause.CurrentTable { if v.Name == clause.CurrentTable {
@ -110,44 +106,41 @@ func (stmt Statement) Quote(field interface{}) string {
} }
// Write write string // Write write string
func (stmt *Statement) AddVar(vars ...interface{}) string { func (stmt *Statement) AddVar(writer clause.Writer, vars ...interface{}) {
stmt.placeholders = strings.Builder{}
stmt.placeholders.Reset()
for idx, v := range vars { for idx, v := range vars {
if idx > 0 { if idx > 0 {
stmt.placeholders.WriteByte(',') writer.WriteByte(',')
} }
switch v := v.(type) { switch v := v.(type) {
case sql.NamedArg: case sql.NamedArg:
if len(v.Name) > 0 { if len(v.Name) > 0 {
stmt.NamedVars = append(stmt.NamedVars, v) stmt.NamedVars = append(stmt.NamedVars, v)
stmt.placeholders.WriteByte('@') writer.WriteByte('@')
stmt.placeholders.WriteString(v.Name) writer.WriteString(v.Name)
} else { } else {
stmt.Vars = append(stmt.Vars, v.Value) stmt.Vars = append(stmt.Vars, v.Value)
stmt.placeholders.WriteString(stmt.DB.Dialector.BindVar(stmt, v.Value)) writer.WriteString(stmt.DB.Dialector.BindVar(stmt, v.Value))
} }
case clause.Column, clause.Table: case clause.Column, clause.Table:
stmt.placeholders.WriteString(stmt.Quote(v)) stmt.QuoteTo(writer, v)
case clause.Expr: case clause.Expr:
stmt.placeholders.WriteString(v.SQL) writer.WriteString(v.SQL)
stmt.Vars = append(stmt.Vars, v.Vars...) stmt.Vars = append(stmt.Vars, v.Vars...)
case []interface{}: case []interface{}:
if len(v) > 0 { if len(v) > 0 {
stmt.placeholders.WriteByte('(') writer.WriteByte('(')
stmt.placeholders.WriteString(stmt.AddVar(v...)) stmt.skipResetPlacehodler = true
stmt.placeholders.WriteByte(')') stmt.AddVar(writer, v...)
writer.WriteByte(')')
} else { } else {
stmt.placeholders.WriteString("(NULL)") writer.WriteString("(NULL)")
} }
default: default:
stmt.Vars = append(stmt.Vars, v) stmt.Vars = append(stmt.Vars, v)
stmt.placeholders.WriteString(stmt.DB.Dialector.BindVar(stmt, v)) writer.WriteString(stmt.DB.Dialector.BindVar(stmt, v))
} }
} }
return stmt.placeholders.String()
} }
// AddClause add clause // AddClause add clause

View File

@ -1,9 +1,8 @@
package tests package tests
import ( import (
"strings"
"github.com/jinzhu/gorm" "github.com/jinzhu/gorm"
"github.com/jinzhu/gorm/clause"
"github.com/jinzhu/gorm/logger" "github.com/jinzhu/gorm/logger"
"github.com/jinzhu/gorm/schema" "github.com/jinzhu/gorm/schema"
) )
@ -23,10 +22,10 @@ func (DummyDialector) BindVar(stmt *gorm.Statement, v interface{}) string {
return "?" return "?"
} }
func (DummyDialector) QuoteTo(builder *strings.Builder, str string) { func (DummyDialector) QuoteTo(writer clause.Writer, str string) {
builder.WriteByte('`') writer.WriteByte('`')
builder.WriteString(str) writer.WriteString(str)
builder.WriteByte('`') writer.WriteByte('`')
} }
func (DummyDialector) Explain(sql string, vars ...interface{}) string { func (DummyDialector) Explain(sql string, vars ...interface{}) string {