Skip to content

Commit

Permalink
dialect: support quoting column names
Browse files Browse the repository at this point in the history
Add support for quoting column names when generating queries. If a table
uses a reserved word for a column name (silly but happens) then there
will be a SQL error when trying to fetch a mapped struct.
  • Loading branch information
Damien Churchill committed Jan 10, 2013
1 parent 37a9cb3 commit d0f6657
Show file tree
Hide file tree
Showing 2 changed files with 49 additions and 33 deletions.
16 changes: 16 additions & 0 deletions dialect.go
Original file line number Diff line number Diff line change
Expand Up @@ -33,6 +33,10 @@ type Dialect interface {
// i is a zero based index of the bind variable in this statement
//
BindVar(i int) string

// Handles quoting of a field name to ensure that it doesn't raise any
// SQL parsing exceptions by using a reserved word as a field name.
QuoteField(field string) string
}

///////////////////////////////////////////////////////
Expand Down Expand Up @@ -91,6 +95,10 @@ func (d SqliteDialect) LastInsertId(res *sql.Result, table *TableMap, exec SqlEx
return (*res).LastInsertId()
}

func (d SqliteDialect) QuoteField(f string) string {
return "'" + f + "'"
}

///////////////////////////////////////////////////////
// PostgreSQL //
////////////////
Expand Down Expand Up @@ -167,6 +175,10 @@ func (d PostgresDialect) LastInsertId(res *sql.Result, table *TableMap, exec Sql
return 0, errors.New(fmt.Sprintf("PostgresDialect: %s did not return a row", sql))
}

func (d PostgresDialect) QuoteField(f string) string {
return `"` + f + `"`
}

///////////////////////////////////////////////////////
// MySQL //
///////////
Expand Down Expand Up @@ -230,3 +242,7 @@ func (m MySQLDialect) BindVar(i int) string {
func (m MySQLDialect) LastInsertId(res *sql.Result, table *TableMap, exec SqlExecutor) (int64, error) {
return (*res).LastInsertId()
}

func (d MySQLDialect) QuoteField(f string) string {
return "`" + f + "`"
}
66 changes: 33 additions & 33 deletions gorp.go
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,7 @@
// license that can be found in the LICENSE file.

// Package gorp provides a simple way to marshal Go structs to and from
// SQL databases. It uses the database/sql package, and should work with any
// SQL databases. It uses the database/sql package, and should work with any
// compliant database/sql driver.
//
// Source code and project home:
Expand All @@ -24,7 +24,7 @@ import (
var zeroVal reflect.Value
var versFieldConst = "[gorp_ver_field]"

// OptimisticLockError is returned by Update() or Delete() if the
// OptimisticLockError is returned by Update() or Delete() if the
// struct being modified has a Version field and the value is not equal to
// the current value in the database
type OptimisticLockError struct {
Expand All @@ -34,9 +34,9 @@ type OptimisticLockError struct {
// Primary key values of the row being updated/deleted
Keys []interface{}

// true if a row was found with those keys, indicating the
// true if a row was found with those keys, indicating the
// LocalVersion is stale. false if no value was found with those
// keys, suggesting the row has been deleted since loaded, or
// keys, suggesting the row has been deleted since loaded, or
// was never inserted to begin with
RowExists bool

Expand All @@ -55,10 +55,10 @@ func (e OptimisticLockError) Error() string {
}

// DbMap is the root gorp mapping object. Create one of these for each
// database schema you wish to map. Each DbMap contains a list of
// database schema you wish to map. Each DbMap contains a list of
// mapped tables.
//
// Example:
// Example:
//
// dialect := gorp.MySQLDialect{"InnoDB", "UTF8"}
// dbmap := &gorp.DbMap{Db: db, Dialect: dialect}
Expand Down Expand Up @@ -91,8 +91,8 @@ type TableMap struct {
dbmap *DbMap
}

// ResetSql removes cached insert/update/select/delete SQL strings
// associated with this TableMap. Call this if you've modified
// ResetSql removes cached insert/update/select/delete SQL strings
// associated with this TableMap. Call this if you've modified
// any column names or the table name itself.
func (t *TableMap) ResetSql() {
t.insertPlan = bindPlan{}
Expand All @@ -101,7 +101,7 @@ func (t *TableMap) ResetSql() {
t.getPlan = bindPlan{}
}

// SetKeys lets you specify the fields on a struct that map to primary
// SetKeys lets you specify the fields on a struct that map to primary
// key columns on the table. If isAutoIncr is set, result.LastInsertId()
// will be used after INSERT to bind the generated id to the Go struct.
//
Expand Down Expand Up @@ -358,7 +358,7 @@ func (t *TableMap) bindGet() bindPlan {
if x > 0 {
s.WriteString(",")
}
s.WriteString(col.ColumnName)
s.WriteString(t.dbmap.Dialect.QuoteField(col.ColumnName))
plan.argFields = append(plan.argFields, col.fieldName)
x++
}
Expand All @@ -371,7 +371,7 @@ func (t *TableMap) bindGet() bindPlan {
if x > 0 {
s.WriteString(" and ")
}
s.WriteString(col.ColumnName)
s.WriteString(t.dbmap.Dialect.QuoteField(col.ColumnName))
s.WriteString("=")
s.WriteString(t.dbmap.Dialect.BindVar(x))

Expand All @@ -388,7 +388,7 @@ func (t *TableMap) bindGet() bindPlan {

// ColumnMap represents a mapping between a Go struct field and a single
// column in a table.
// Unique and MaxSize only inform the
// Unique and MaxSize only inform the
// CreateTables() function and are not used by Insert/Update/Delete/Get.
type ColumnMap struct {
// Column name in db table
Expand Down Expand Up @@ -443,7 +443,7 @@ func (c *ColumnMap) SetMaxSize(size int) *ColumnMap {
return c
}

// Transaction represents a database transaction.
// Transaction represents a database transaction.
// Insert/Update/Delete/Get/Exec operations will be run in the context
// of that transaction. Transactions should be terminated with
// a call to Commit() or Rollback()
Expand Down Expand Up @@ -472,7 +472,7 @@ type SqlExecutor interface {

// TraceOn turns on SQL statement logging for this DbMap. After this is
// called, all SQL statements will be sent to the logger. If prefix is
// a non-empty string, it will be written to the front of all logged
// a non-empty string, it will be written to the front of all logged
// strings, which can aid in filtering log lines.
//
// Use TraceOn if you want to spy on the SQL statements that gorp
Expand All @@ -494,16 +494,16 @@ func (m *DbMap) TraceOff() {

// AddTable registers the given interface type with gorp. The table name
// will be given the name of the TypeOf(i). You must call this function,
// or AddTableWithName, for any struct type you wish to persist with
// or AddTableWithName, for any struct type you wish to persist with
// the given DbMap.
//
// This operation is idempotent. If i's type is already mapped, the
// This operation is idempotent. If i's type is already mapped, the
// existing *TableMap is returned
func (m *DbMap) AddTable(i interface{}) *TableMap {
return m.AddTableWithName(i, "")
}

// AddTableWithName has the same behavior as AddTable, but sets
// AddTableWithName has the same behavior as AddTable, but sets
// table.TableName to name.
func (m *DbMap) AddTableWithName(i interface{}, name string) *TableMap {
t := reflect.TypeOf(i)
Expand Down Expand Up @@ -550,7 +550,7 @@ func (m *DbMap) AddTableWithName(i interface{}, name string) *TableMap {

// CreateTables iterates through TableMaps registered to this DbMap and
// executes "create table" statements against the database for each.
//
//
// This is particularly useful in unit tests where you want to create
// and destroy the schema automatically.
func (m *DbMap) CreateTables() error {
Expand Down Expand Up @@ -620,24 +620,24 @@ func (m *DbMap) DropTables() error {
return err
}

// Insert runs a SQL INSERT statement for each element in list. List
// Insert runs a SQL INSERT statement for each element in list. List
// items must be pointers.
//
// Any interface whose TableMap has an auto-increment primary key will
// have its last insert id bound to the PK field on the struct.
//
// Hook functions PreInsert() and/or PostInsert() will be executed
// Hook functions PreInsert() and/or PostInsert() will be executed
// before/after the INSERT statement if the interface defines them.
//
// Panics if any interface in the list has not been registered with AddTable
func (m *DbMap) Insert(list ...interface{}) error {
return insert(m, m, list...)
}

// Update runs a SQL UPDATE statement for each element in list. List
// items must be pointers.
// Update runs a SQL UPDATE statement for each element in list. List
// items must be pointers.
//
// Hook functions PreUpdate() and/or PostUpdate() will be executed
// Hook functions PreUpdate() and/or PostUpdate() will be executed
// before/after the UPDATE statement if the interface defines them.
//
// Returns number of rows updated
Expand All @@ -648,11 +648,11 @@ func (m *DbMap) Update(list ...interface{}) (int64, error) {
return update(m, m, list...)
}

// Delete runs a SQL DELETE statement for each element in list. List
// Delete runs a SQL DELETE statement for each element in list. List
// items must be pointers.
//
// Hook functions PreDelete() and/or PostDelete() will be executed
// before/after the DELETE statement if the interface defines them.
// Hook functions PreDelete() and/or PostDelete() will be executed
// before/after the DELETE statement if the interface defines them.
//
// Returns number of rows deleted
//
Expand All @@ -666,11 +666,11 @@ func (m *DbMap) Delete(list ...interface{}) (int64, error) {
// primary key(s)
//
// i should be an empty value for the struct to load
// keys should be the primary key value(s) for the row to load. If
// multiple keys exist on the table, the order should match the column
// keys should be the primary key value(s) for the row to load. If
// multiple keys exist on the table, the order should match the column
// order specified in SetKeys() when the table mapping was defined.
//
// Hook function PostGet() will be executed
// Hook function PostGet() will be executed
// after the SELECT statement if the interface defines them.
//
// Returns a pointer to a struct that matches or nil if no row is found
Expand All @@ -682,15 +682,15 @@ func (m *DbMap) Get(i interface{}, keys ...interface{}) (interface{}, error) {
}

// Select runs an arbitrary SQL query, binding the columns in the result
// to fields on the struct specified by i. args represent the bind
// to fields on the struct specified by i. args represent the bind
// parameters for the SQL statement.
//
// Column names on the SELECT statement should be aliased to the field names
// on the struct i. Returns an error if one or more columns in the result
// do not match. It is OK if fields on i are not part of the SQL
// do not match. It is OK if fields on i are not part of the SQL
// statement.
//
// Hook function PostGet() will be executed
// Hook function PostGet() will be executed
// after the SELECT statement if the interface defines them.
//
// Returns a slice of pointers to matching rows of type i.
Expand Down Expand Up @@ -878,7 +878,7 @@ func rawselect(m *DbMap, exec SqlExecutor, i interface{}, query string,
return nil, err
}

// check if type t is a mapped table - if so we'll
// check if type t is a mapped table - if so we'll
// check the table for column aliasing below
tableMapped := false
table := tableOrNil(m, t)
Expand Down

0 comments on commit d0f6657

Please sign in to comment.