diff --git a/bdb/drivers/mock.go b/bdb/drivers/mock.go index 90d4e54..6e0b8e1 100644 --- a/bdb/drivers/mock.go +++ b/bdb/drivers/mock.go @@ -123,3 +123,18 @@ func (m *MockDriver) Open() error { return nil } // Close mimics a database close call 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 +} diff --git a/bdb/drivers/mysql.go b/bdb/drivers/mysql.go index ee6dfe6..d269560 100644 --- a/bdb/drivers/mysql.go +++ b/bdb/drivers/mysql.go @@ -318,3 +318,18 @@ func mySQLIsValidated(typ string) bool { 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 +} diff --git a/bdb/drivers/postgres.go b/bdb/drivers/postgres.go index 7c27d47..c39e3f5 100644 --- a/bdb/drivers/postgres.go +++ b/bdb/drivers/postgres.go @@ -340,3 +340,18 @@ func psqlIsValidated(typ string) bool { 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 +} diff --git a/bdb/interface.go b/bdb/interface.go index b4d6b7a..d26ef54 100644 --- a/bdb/interface.go +++ b/bdb/interface.go @@ -22,6 +22,13 @@ type Interface interface { Open() error // Close the database connection 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 diff --git a/boil/query.go b/boil/query.go index 2ea37e9..195e0b2 100644 --- a/boil/query.go +++ b/boil/query.go @@ -19,6 +19,7 @@ const ( // Query holds the state for the built up query type Query struct { executor Executor + dialect *Dialect plainSQL plainSQL load []string delete bool @@ -37,6 +38,20 @@ type Query struct { 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 { clause string orSeparator bool @@ -121,6 +136,11 @@ func GetExecutor(q *Query) Executor { return q.executor } +// SetDialect on the query. +func SetDialect(q *Query, dialect *Dialect) { + q.dialect = dialect +} + // SetSQL on the query. func SetSQL(q *Query, sql string, args ...interface{}) { q.plainSQL = plainSQL{sql: sql, args: args} diff --git a/sqlboiler.go b/sqlboiler.go index 0234cd8..d6e1bb2 100644 --- a/sqlboiler.go +++ b/sqlboiler.go @@ -32,8 +32,9 @@ const ( type State struct { Config *Config - Driver bdb.Interface - Tables []bdb.Table + Driver bdb.Interface + Tables []bdb.Table + Dialect boil.Dialect Templates *templateList TestTemplates *templateList @@ -102,6 +103,7 @@ func (s *State) Run(includeTests bool) error { PkgName: s.Config.PkgName, NoHooks: s.Config.NoHooks, NoAutoTimestamps: s.Config.NoAutoTimestamps, + Dialect: s.Dialect, StringFuncs: templateStringMappers, } @@ -135,6 +137,7 @@ func (s *State) Run(includeTests bool) error { NoHooks: s.Config.NoHooks, NoAutoTimestamps: s.Config.NoAutoTimestamps, Tags: s.Config.Tags, + Dialect: s.Dialect, StringFuncs: templateStringMappers, } @@ -246,6 +249,10 @@ func (s *State) initDriver(driverName string) error { 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 } diff --git a/strmangle/strmangle.go b/strmangle/strmangle.go index 3c1fa87..e62f83a 100644 --- a/strmangle/strmangle.go +++ b/strmangle/strmangle.go @@ -46,6 +46,18 @@ func SchemaTable(driver string, schema string, table string) string { 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 func IdentQuote(s string) string { if strings.ToLower(s) == "null" { diff --git a/templates.go b/templates.go index 8d171f2..ce94251 100644 --- a/templates.go +++ b/templates.go @@ -8,6 +8,7 @@ import ( "text/template" "github.com/vattle/sqlboiler/bdb" + "github.com/vattle/sqlboiler/boil" "github.com/vattle/sqlboiler/strmangle" ) @@ -22,8 +23,8 @@ type templateData struct { NoHooks bool NoAutoTimestamps bool Tags []string - - StringFuncs map[string]func(string) string + StringFuncs map[string]func(string) string + Dialect boil.Dialect } type templateList struct { @@ -116,6 +117,7 @@ var templateFunctions = template.FuncMap{ // String ops "quoteWrap": func(a string) string { return fmt.Sprintf(`"%s"`, a) }, "id": strmangle.Identifier, + "wrapQuote": strmangle.WrapQuote, // Pluralization "singular": strmangle.Singular, diff --git a/templates/singleton/boil_queries.tpl b/templates/singleton/boil_queries.tpl index 6cb607d..162c764 100644 --- a/templates/singleton/boil_queries.tpl +++ b/templates/singleton/boil_queries.tpl @@ -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 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 func NewQuery(exec boil.Executor, mods ...qm.QueryMod) *boil.Query { - q := &boil.Query{} - boil.SetExecutor(q, exec) - qm.Apply(q, mods...) + q := &boil.Query{} + boil.SetExecutor(q, exec) + boil.SetDialect(q, &dialect) + qm.Apply(q, mods...) - return q + return q }