Add quote dialects

This commit is contained in:
Patrick O'brien 2016-09-10 03:14:18 +10:00
parent 817189fbfd
commit 9e6a3d5ee3
9 changed files with 109 additions and 9 deletions

View file

@ -123,3 +123,18 @@ func (m *MockDriver) Open() error { return nil }
// Close mimics a database close call // Close mimics a database close call
func (m *MockDriver) Close() {} func (m *MockDriver) Close() {}
// RightQuote is the quoting character for the right side of the identifier
func (m *MockDriver) RightQuote() string {
return "`"
}
// LeftQuote is the quoting character for the left side of the identifier
func (m *MockDriver) LeftQuote() string {
return `"`
}
// IndexPlaceholders returns true to indicate fake support of indexed placeholders
func (m *MockDriver) IndexPlaceholders() bool {
return false
}

View file

@ -318,3 +318,18 @@ func mySQLIsValidated(typ string) bool {
return false return false
} }
// RightQuote is the quoting character for the right side of the identifier
func (m *MySQLDriver) RightQuote() string {
return "`"
}
// LeftQuote is the quoting character for the left side of the identifier
func (m *MySQLDriver) LeftQuote() string {
return "`"
}
// IndexPlaceholders returns false to indicate MySQL doesnt support indexed placeholders
func (m *MySQLDriver) IndexPlaceholders() bool {
return false
}

View file

@ -340,3 +340,18 @@ func psqlIsValidated(typ string) bool {
return false return false
} }
// RightQuote is the quoting character for the right side of the identifier
func (p *PostgresDriver) RightQuote() string {
return `"`
}
// LeftQuote is the quoting character for the left side of the identifier
func (p *PostgresDriver) LeftQuote() string {
return `"`
}
// IndexPlaceholders returns true to indicate PSQL supports indexed placeholders
func (p *PostgresDriver) IndexPlaceholders() bool {
return true
}

View file

@ -22,6 +22,13 @@ type Interface interface {
Open() error Open() error
// Close the database connection // Close the database connection
Close() Close()
// Dialect helpers, these provide the values that will go into
// a boil.Dialect, so the query builder knows how to support
// your database driver properly.
LeftQuote() string
RightQuote() string
IndexPlaceholders() bool
} }
// Tables returns the metadata for all tables, minus the tables // Tables returns the metadata for all tables, minus the tables

View file

@ -19,6 +19,7 @@ const (
// Query holds the state for the built up query // Query holds the state for the built up query
type Query struct { type Query struct {
executor Executor executor Executor
dialect *Dialect
plainSQL plainSQL plainSQL plainSQL
load []string load []string
delete bool delete bool
@ -37,6 +38,20 @@ type Query struct {
forlock string forlock string
} }
// Dialect holds values that direct the query builder
// how to build compatible queries for each database.
// Each database driver needs to implement functions
// that provide these values.
type Dialect struct {
// The left quote character for SQL identifiers
LQ string
// The right quote character for SQL identifiers
RQ string
// Bool flag indicating whether indexed
// placeholders ($1) are used, or ? placeholders.
IndexPlaceholders bool
}
type where struct { type where struct {
clause string clause string
orSeparator bool orSeparator bool
@ -121,6 +136,11 @@ func GetExecutor(q *Query) Executor {
return q.executor return q.executor
} }
// SetDialect on the query.
func SetDialect(q *Query, dialect *Dialect) {
q.dialect = dialect
}
// SetSQL on the query. // SetSQL on the query.
func SetSQL(q *Query, sql string, args ...interface{}) { func SetSQL(q *Query, sql string, args ...interface{}) {
q.plainSQL = plainSQL{sql: sql, args: args} q.plainSQL = plainSQL{sql: sql, args: args}

View file

@ -32,8 +32,9 @@ const (
type State struct { type State struct {
Config *Config Config *Config
Driver bdb.Interface Driver bdb.Interface
Tables []bdb.Table Tables []bdb.Table
Dialect boil.Dialect
Templates *templateList Templates *templateList
TestTemplates *templateList TestTemplates *templateList
@ -102,6 +103,7 @@ func (s *State) Run(includeTests bool) error {
PkgName: s.Config.PkgName, PkgName: s.Config.PkgName,
NoHooks: s.Config.NoHooks, NoHooks: s.Config.NoHooks,
NoAutoTimestamps: s.Config.NoAutoTimestamps, NoAutoTimestamps: s.Config.NoAutoTimestamps,
Dialect: s.Dialect,
StringFuncs: templateStringMappers, StringFuncs: templateStringMappers,
} }
@ -135,6 +137,7 @@ func (s *State) Run(includeTests bool) error {
NoHooks: s.Config.NoHooks, NoHooks: s.Config.NoHooks,
NoAutoTimestamps: s.Config.NoAutoTimestamps, NoAutoTimestamps: s.Config.NoAutoTimestamps,
Tags: s.Config.Tags, Tags: s.Config.Tags,
Dialect: s.Dialect,
StringFuncs: templateStringMappers, StringFuncs: templateStringMappers,
} }
@ -246,6 +249,10 @@ func (s *State) initDriver(driverName string) error {
return errors.New("An invalid driver name was provided") return errors.New("An invalid driver name was provided")
} }
s.Dialect.LQ = s.Driver.LeftQuote()
s.Dialect.RQ = s.Driver.RightQuote()
s.Dialect.IndexPlaceholders = s.Driver.IndexPlaceholders()
return nil return nil
} }

View file

@ -46,6 +46,18 @@ func SchemaTable(driver string, schema string, table string) string {
return fmt.Sprintf(`"%s"`, table) return fmt.Sprintf(`"%s"`, table)
} }
// WrapQuote wraps a quote character in quotes.
func WrapQuote(s string) string {
if s == `"` {
return "`\"`"
}
if s == "`" {
return "\"`\""
}
return fmt.Sprintf("`%s`", s)
}
// IdentQuote attempts to quote simple identifiers in SQL tatements // IdentQuote attempts to quote simple identifiers in SQL tatements
func IdentQuote(s string) string { func IdentQuote(s string) string {
if strings.ToLower(s) == "null" { if strings.ToLower(s) == "null" {

View file

@ -8,6 +8,7 @@ import (
"text/template" "text/template"
"github.com/vattle/sqlboiler/bdb" "github.com/vattle/sqlboiler/bdb"
"github.com/vattle/sqlboiler/boil"
"github.com/vattle/sqlboiler/strmangle" "github.com/vattle/sqlboiler/strmangle"
) )
@ -22,8 +23,8 @@ type templateData struct {
NoHooks bool NoHooks bool
NoAutoTimestamps bool NoAutoTimestamps bool
Tags []string Tags []string
StringFuncs map[string]func(string) string
StringFuncs map[string]func(string) string Dialect boil.Dialect
} }
type templateList struct { type templateList struct {
@ -116,6 +117,7 @@ var templateFunctions = template.FuncMap{
// String ops // String ops
"quoteWrap": func(a string) string { return fmt.Sprintf(`"%s"`, a) }, "quoteWrap": func(a string) string { return fmt.Sprintf(`"%s"`, a) },
"id": strmangle.Identifier, "id": strmangle.Identifier,
"wrapQuote": strmangle.WrapQuote,
// Pluralization // Pluralization
"singular": strmangle.Singular, "singular": strmangle.Singular,

View file

@ -1,13 +1,20 @@
var dialect boil.Dialect = boil.Dialect{
LQ: {{wrapQuote .Dialect.LQ}},
RQ: {{wrapQuote .Dialect.RQ}},
IndexPlaceholders: {{.Dialect.IndexPlaceholders}},
}
// NewQueryG initializes a new Query using the passed in QueryMods // NewQueryG initializes a new Query using the passed in QueryMods
func NewQueryG(mods ...qm.QueryMod) *boil.Query { func NewQueryG(mods ...qm.QueryMod) *boil.Query {
return NewQuery(boil.GetDB(), mods...) return NewQuery(boil.GetDB(), mods...)
} }
// NewQuery initializes a new Query using the passed in QueryMods // NewQuery initializes a new Query using the passed in QueryMods
func NewQuery(exec boil.Executor, mods ...qm.QueryMod) *boil.Query { func NewQuery(exec boil.Executor, mods ...qm.QueryMod) *boil.Query {
q := &boil.Query{} q := &boil.Query{}
boil.SetExecutor(q, exec) boil.SetExecutor(q, exec)
qm.Apply(q, mods...) boil.SetDialect(q, &dialect)
qm.Apply(q, mods...)
return q return q
} }