Add TitleCase cache

This commit is contained in:
Patrick O'brien 2016-09-02 17:09:42 +10:00
parent b101df0a24
commit 09fb8005f6
13 changed files with 69 additions and 103 deletions

View file

@ -20,7 +20,7 @@ same SQL helpers and wrappers for every project we were creating, but did not wa
there that utilize the "code-first" approach. `SQLX` is a great project, but very minimalistic and still requires a there that utilize the "code-first" approach. `SQLX` is a great project, but very minimalistic and still requires a
considerable amount of boilerplate for every project. Originally this project started as a SQL boilerplate generator (hence the name) considerable amount of boilerplate for every project. Originally this project started as a SQL boilerplate generator (hence the name)
that generated simple helper functions, but we found that we could accomplish the same task by turning it into a that generated simple helper functions, but we found that we could accomplish the same task by turning it into a
(mostly) fully fledged ORM, without any sacrifice in performance or congruency, but generous gains in flexibility. (mostly) fully fledged ORM generator, without any sacrifice in performance or congruency, but generous gains in flexibility.
The approach we've taken has afforded us the following benefits: The approach we've taken has afforded us the following benefits:

View file

@ -74,18 +74,12 @@ func (q *Query) BindP(obj interface{}) {
// For custom objects that want to use eager loading, please see the // For custom objects that want to use eager loading, please see the
// loadRelationships function. // loadRelationships function.
func Bind(rows *sql.Rows, obj interface{}) error { func Bind(rows *sql.Rows, obj interface{}) error {
return BindFast(rows, obj, nil)
}
// BindFast uses a lookup table for column_name to ColumnName to avoid TitleCase.
func BindFast(rows *sql.Rows, obj interface{}, titleCases map[string]string) error {
structType, sliceType, singular, err := bindChecks(obj) structType, sliceType, singular, err := bindChecks(obj)
if err != nil { if err != nil {
return err return err
} }
return bind(rows, obj, structType, sliceType, singular, titleCases) return bind(rows, obj, structType, sliceType, singular)
} }
// Bind executes the query and inserts the // Bind executes the query and inserts the
@ -93,11 +87,6 @@ func BindFast(rows *sql.Rows, obj interface{}, titleCases map[string]string) err
// //
// See documentation for boil.Bind() // See documentation for boil.Bind()
func (q *Query) Bind(obj interface{}) error { func (q *Query) Bind(obj interface{}) error {
return q.BindFast(obj, nil)
}
// BindFast uses a lookup table for column_name to ColumnName to avoid TitleCase.
func (q *Query) BindFast(obj interface{}, titleCases map[string]string) error {
structType, sliceType, singular, err := bindChecks(obj) structType, sliceType, singular, err := bindChecks(obj)
if err != nil { if err != nil {
return err return err
@ -108,8 +97,7 @@ func (q *Query) BindFast(obj interface{}, titleCases map[string]string) error {
return errors.Wrap(err, "bind failed to execute query") return errors.Wrap(err, "bind failed to execute query")
} }
defer rows.Close() defer rows.Close()
if res := bind(rows, obj, structType, sliceType, singular); res != nil {
if res := bind(rows, obj, structType, sliceType, singular, titleCases); res != nil {
return res return res
} }
@ -255,7 +243,7 @@ func bindChecks(obj interface{}) (structType reflect.Type, sliceType reflect.Typ
return structType, sliceType, singular, nil return structType, sliceType, singular, nil
} }
func bind(rows *sql.Rows, obj interface{}, structType, sliceType reflect.Type, singular bool, titleCases map[string]string) error { func bind(rows *sql.Rows, obj interface{}, structType, sliceType reflect.Type, singular bool) error {
cols, err := rows.Columns() cols, err := rows.Columns()
if err != nil { if err != nil {
return errors.Wrap(err, "bind failed to get column names") return errors.Wrap(err, "bind failed to get column names")
@ -275,7 +263,7 @@ func bind(rows *sql.Rows, obj interface{}, structType, sliceType reflect.Type, s
mut.RUnlock() mut.RUnlock()
if !ok { if !ok {
mapping, err = bindMapping(structType, titleCases, cols) mapping, err = bindMapping(structType, cols)
if err != nil { if err != nil {
return err return err
} }
@ -317,13 +305,13 @@ func bind(rows *sql.Rows, obj interface{}, structType, sliceType reflect.Type, s
return nil return nil
} }
func bindMapping(typ reflect.Type, titleCases map[string]string, cols []string) ([]uint64, error) { func bindMapping(typ reflect.Type, cols []string) ([]uint64, error) {
ptrs := make([]uint64, len(cols)) ptrs := make([]uint64, len(cols))
mapping := makeStructMapping(typ, titleCases) mapping := makeStructMapping(typ)
ColLoop: ColLoop:
for i, c := range cols { for i, c := range cols {
name := strmangle.TitleCaseIdentifier(c, titleCases) name := strmangle.TitleCaseIdentifier(c)
ptrMap, ok := mapping[name] ptrMap, ok := mapping[name]
if ok { if ok {
ptrs[i] = ptrMap ptrs[i] = ptrMap
@ -376,13 +364,13 @@ func ptrFromMapping(val reflect.Value, mapping uint64) reflect.Value {
panic("could not find pointer from mapping") panic("could not find pointer from mapping")
} }
func makeStructMapping(typ reflect.Type, titleCases map[string]string) map[string]uint64 { func makeStructMapping(typ reflect.Type) map[string]uint64 {
fieldMaps := make(map[string]uint64) fieldMaps := make(map[string]uint64)
makeStructMappingHelper(typ, "", 0, 0, fieldMaps, titleCases) makeStructMappingHelper(typ, "", 0, 0, fieldMaps)
return fieldMaps return fieldMaps
} }
func makeStructMappingHelper(typ reflect.Type, prefix string, current uint64, depth uint, fieldMaps map[string]uint64, titleCases map[string]string) { func makeStructMappingHelper(typ reflect.Type, prefix string, current uint64, depth uint, fieldMaps map[string]uint64) {
if typ.Kind() == reflect.Ptr { if typ.Kind() == reflect.Ptr {
typ = typ.Elem() typ = typ.Elem()
} }
@ -391,7 +379,7 @@ func makeStructMappingHelper(typ reflect.Type, prefix string, current uint64, de
for i := 0; i < n; i++ { for i := 0; i < n; i++ {
f := typ.Field(i) f := typ.Field(i)
tag, recurse := getBoilTag(f, titleCases) tag, recurse := getBoilTag(f)
if len(tag) == 0 { if len(tag) == 0 {
tag = f.Name tag = f.Name
} else if tag[0] == '-' { } else if tag[0] == '-' {
@ -403,7 +391,7 @@ func makeStructMappingHelper(typ reflect.Type, prefix string, current uint64, de
} }
if recurse { if recurse {
makeStructMappingHelper(f.Type, tag, current|uint64(i)<<depth, depth+8, fieldMaps, titleCases) makeStructMappingHelper(f.Type, tag, current|uint64(i)<<depth, depth+8, fieldMaps)
continue continue
} }
@ -411,7 +399,7 @@ func makeStructMappingHelper(typ reflect.Type, prefix string, current uint64, de
} }
} }
func getBoilTag(field reflect.StructField, titleCases map[string]string) (name string, recurse bool) { func getBoilTag(field reflect.StructField) (name string, recurse bool) {
tag := field.Tag.Get("boil") tag := field.Tag.Get("boil")
name = field.Name name = field.Name
@ -419,24 +407,15 @@ func getBoilTag(field reflect.StructField, titleCases map[string]string) (name s
return name, false return name, false
} }
var ok bool
ind := strings.IndexByte(tag, ',') ind := strings.IndexByte(tag, ',')
if ind == -1 { if ind == -1 {
name, ok = titleCases[tag] return strmangle.TitleCase(tag), false
if !ok {
name = strmangle.TitleCase(tag)
}
return name, false
} else if ind == 0 { } else if ind == 0 {
return name, true return name, true
} }
nameFragment := tag[:ind] nameFragment := tag[:ind]
name, ok = titleCases[nameFragment] return strmangle.TitleCase(nameFragment), true
if !ok {
name = strmangle.TitleCase(nameFragment)
}
return name, true
} }
func makeCacheKey(typ string, cols []string) string { func makeCacheKey(typ string, cols []string) string {
@ -452,17 +431,12 @@ func makeCacheKey(typ string, cols []string) string {
} }
// GetStructValues returns the values (as interface) of the matching columns in obj // GetStructValues returns the values (as interface) of the matching columns in obj
func GetStructValues(obj interface{}, titleCases map[string]string, columns ...string) []interface{} { func GetStructValues(obj interface{}, columns ...string) []interface{} {
ret := make([]interface{}, len(columns)) ret := make([]interface{}, len(columns))
val := reflect.Indirect(reflect.ValueOf(obj)) val := reflect.Indirect(reflect.ValueOf(obj))
for i, c := range columns { for i, c := range columns {
var fieldName string fieldName := strmangle.TitleCase(c)
if titleCases == nil {
fieldName = strmangle.TitleCase(c)
} else {
fieldName = titleCases[c]
}
field := val.FieldByName(fieldName) field := val.FieldByName(fieldName)
if !field.IsValid() { if !field.IsValid() {
panic(fmt.Sprintf("unable to find field with name: %s\n%#v", fieldName, obj)) panic(fmt.Sprintf("unable to find field with name: %s\n%#v", fieldName, obj))
@ -474,19 +448,13 @@ func GetStructValues(obj interface{}, titleCases map[string]string, columns ...s
} }
// GetSliceValues returns the values (as interface) of the matching columns in obj. // GetSliceValues returns the values (as interface) of the matching columns in obj.
func GetSliceValues(slice []interface{}, titleCases map[string]string, columns ...string) []interface{} { func GetSliceValues(slice []interface{}, columns ...string) []interface{} {
ret := make([]interface{}, len(slice)*len(columns)) ret := make([]interface{}, len(slice)*len(columns))
for i, obj := range slice { for i, obj := range slice {
val := reflect.Indirect(reflect.ValueOf(obj)) val := reflect.Indirect(reflect.ValueOf(obj))
for j, c := range columns { for j, c := range columns {
var fieldName string fieldName := strmangle.TitleCase(c)
if titleCases == nil {
fieldName = strmangle.TitleCase(c)
} else {
fieldName = titleCases[c]
}
field := val.FieldByName(fieldName) field := val.FieldByName(fieldName)
if !field.IsValid() { if !field.IsValid() {
panic(fmt.Sprintf("unable to find field with name: %s\n%#v", fieldName, obj)) panic(fmt.Sprintf("unable to find field with name: %s\n%#v", fieldName, obj))
@ -499,7 +467,7 @@ func GetSliceValues(slice []interface{}, titleCases map[string]string, columns .
} }
// GetStructPointers returns a slice of pointers to the matching columns in obj // GetStructPointers returns a slice of pointers to the matching columns in obj
func GetStructPointers(obj interface{}, titleCases map[string]string, columns ...string) []interface{} { func GetStructPointers(obj interface{}, columns ...string) []interface{} {
val := reflect.ValueOf(obj).Elem() val := reflect.ValueOf(obj).Elem()
var ln int var ln int
@ -513,14 +481,7 @@ func GetStructPointers(obj interface{}, titleCases map[string]string, columns ..
} else { } else {
ln = len(columns) ln = len(columns)
getField = func(v reflect.Value, i int) reflect.Value { getField = func(v reflect.Value, i int) reflect.Value {
var fieldName string return v.FieldByName(strmangle.TitleCase(columns[i]))
if titleCases == nil {
fieldName = strmangle.TitleCase(columns[i])
} else {
fieldName = titleCases[columns[i]]
}
return v.FieldByName(fieldName)
} }
} }

View file

@ -115,7 +115,7 @@ func TestMakeStructMapping(t *testing.T) {
} `boil:",bind"` } `boil:",bind"`
}{} }{}
got := makeStructMapping(reflect.TypeOf(testStruct), nil) got := makeStructMapping(reflect.TypeOf(testStruct))
expectMap := map[string]uint64{ expectMap := map[string]uint64{
"Different": testMakeMapping(0), "Different": testMakeMapping(0),
@ -189,16 +189,6 @@ func TestGetBoilTag(t *testing.T) {
Nose string Nose string
} }
var testTitleCases = map[string]string{
"test_one": "TestOne",
"test_two": "TestTwo",
"middle_name": "MiddleName",
"awesome_name": "AwesomeName",
"age": "Age",
"face": "Face",
"nose": "Nose",
}
var structFields []reflect.StructField var structFields []reflect.StructField
typ := reflect.TypeOf(TestStruct{}) typ := reflect.TypeOf(TestStruct{})
removeOk := func(thing reflect.StructField, ok bool) reflect.StructField { removeOk := func(thing reflect.StructField, ok bool) reflect.StructField {
@ -228,7 +218,7 @@ func TestGetBoilTag(t *testing.T) {
{"Nose", false}, {"Nose", false},
} }
for i, s := range structFields { for i, s := range structFields {
name, recurse := getBoilTag(s, testTitleCases) name, recurse := getBoilTag(s)
if expect[i].Name != name { if expect[i].Name != name {
t.Errorf("Invalid name, expect %q, got %q", expect[i].Name, name) t.Errorf("Invalid name, expect %q, got %q", expect[i].Name, name)
} }
@ -665,7 +655,7 @@ func TestGetStructValues(t *testing.T) {
NullBool: null.NewBool(true, false), NullBool: null.NewBool(true, false),
} }
vals := GetStructValues(&o, nil, "title_thing", "name", "id", "stuff", "things", "time", "null_bool") vals := GetStructValues(&o, "title_thing", "name", "id", "stuff", "things", "time", "null_bool")
if vals[0].(string) != "patrick" { if vals[0].(string) != "patrick" {
t.Errorf("Want test, got %s", vals[0]) t.Errorf("Want test, got %s", vals[0])
} }
@ -704,7 +694,7 @@ func TestGetSliceValues(t *testing.T) {
in[0] = o[0] in[0] = o[0]
in[1] = o[1] in[1] = o[1]
vals := GetSliceValues(in, nil, "id", "name") vals := GetSliceValues(in, "id", "name")
if got := vals[0].(int); got != 5 { if got := vals[0].(int); got != 5 {
t.Error(got) t.Error(got)
} }
@ -729,7 +719,7 @@ func TestGetStructPointers(t *testing.T) {
Title: "patrick", Title: "patrick",
} }
ptrs := GetStructPointers(&o, nil, "title", "id") ptrs := GetStructPointers(&o, "title", "id")
*ptrs[0].(*string) = "test" *ptrs[0].(*string) = "test"
if o.Title != "test" { if o.Title != "test" {
t.Errorf("Expected test, got %s", o.Title) t.Errorf("Expected test, got %s", o.Title)

View file

@ -9,6 +9,7 @@ import (
"math" "math"
"regexp" "regexp"
"strings" "strings"
"sync"
) )
var ( var (
@ -150,6 +151,13 @@ func Singular(name string) string {
return buf.String() return buf.String()
} }
// titleCaseCache holds the mapping of title cases.
// Example: map["MyWord"] == "my_word"
var (
mut sync.RWMutex
titleCaseCache = map[string]string{}
)
// TitleCase changes a snake-case variable name // TitleCase changes a snake-case variable name
// into a go styled object variable name of "ColumnName". // into a go styled object variable name of "ColumnName".
// titleCase also fully uppercases "ID" components of names, for example // titleCase also fully uppercases "ID" components of names, for example
@ -158,6 +166,14 @@ func Singular(name string) string {
// Note: This method is ugly because it has been highly optimized, // Note: This method is ugly because it has been highly optimized,
// we found that it was a fairly large bottleneck when we were using regexp. // we found that it was a fairly large bottleneck when we were using regexp.
func TitleCase(n string) string { func TitleCase(n string) string {
// Attempt to fetch from cache
mut.RLock()
val, ok := titleCaseCache[n]
mut.RUnlock()
if ok {
return val
}
ln := len(n) ln := len(n)
name := []byte(n) name := []byte(n)
buf := GetBuffer() buf := GetBuffer()
@ -219,6 +235,12 @@ func TitleCase(n string) string {
ret := buf.String() ret := buf.String()
PutBuffer(buf) PutBuffer(buf)
// Cache the title case result
mut.Lock()
titleCaseCache[n] = ret
mut.Unlock()
return ret return ret
} }
@ -264,14 +286,10 @@ func CamelCase(name string) string {
// TitleCaseIdentifier splits on dots and then titlecases each fragment. // TitleCaseIdentifier splits on dots and then titlecases each fragment.
// map titleCase (split c ".") // map titleCase (split c ".")
func TitleCaseIdentifier(id string, titleCases map[string]string) string { func TitleCaseIdentifier(id string) string {
nextDot := strings.IndexByte(id, '.') nextDot := strings.IndexByte(id, '.')
if nextDot < 0 { if nextDot < 0 {
titled, ok := titleCases[id] return TitleCase(id)
if !ok {
titled = TitleCase(id)
}
return titled
} }
buf := GetBuffer() buf := GetBuffer()
@ -283,10 +301,7 @@ func TitleCaseIdentifier(id string, titleCases map[string]string) string {
fmt.Println(lastDot, nextDot) fmt.Println(lastDot, nextDot)
fragment := id[lastDot:nextDot] fragment := id[lastDot:nextDot]
titled, ok := titleCases[fragment] titled := TitleCase(fragment)
if !ok {
titled = TitleCase(fragment)
}
if addDots { if addDots {
buf.WriteByte('.') buf.WriteByte('.')

View file

@ -242,7 +242,7 @@ func TestTitleCaseIdentifier(t *testing.T) {
} }
for i, test := range tests { for i, test := range tests {
if out := TitleCaseIdentifier(test.In, nil); out != test.Out { if out := TitleCaseIdentifier(test.In); out != test.Out {
t.Errorf("[%d] (%s) Out was wrong: %q, want: %q", i, test.In, out, test.Out) t.Errorf("[%d] (%s) Out was wrong: %q, want: %q", i, test.In, out, test.Out)
} }
} }

View file

@ -16,7 +16,7 @@ func (q {{$varNameSingular}}Query) One() (*{{$tableNameSingular}}, error) {
boil.SetLimit(q.Query, 1) boil.SetLimit(q.Query, 1)
err := q.BindFast(o, {{$varNameSingular}}TitleCases) err := q.Bind(o)
if err != nil { if err != nil {
if errors.Cause(err) == sql.ErrNoRows { if errors.Cause(err) == sql.ErrNoRows {
return nil, sql.ErrNoRows return nil, sql.ErrNoRows
@ -47,7 +47,7 @@ func (q {{$varNameSingular}}Query) AllP() {{$tableNameSingular}}Slice {
func (q {{$varNameSingular}}Query) All() ({{$tableNameSingular}}Slice, error) { func (q {{$varNameSingular}}Query) All() ({{$tableNameSingular}}Slice, error) {
var o {{$tableNameSingular}}Slice var o {{$tableNameSingular}}Slice
err := q.BindFast(&o, {{$varNameSingular}}TitleCases) err := q.Bind(&o)
if err != nil { if err != nil {
return nil, errors.Wrap(err, "{{.PkgName}}: failed to assign all query results to {{$tableNameSingular}} slice") return nil, errors.Wrap(err, "{{.PkgName}}: failed to assign all query results to {{$tableNameSingular}} slice")
} }

View file

@ -43,7 +43,7 @@ func ({{$varNameSingular}}L) Load{{.Function.Name}}(e boil.Executor, singular bo
defer results.Close() defer results.Close()
var resultSlice []*{{.ForeignTable.NameGo}} var resultSlice []*{{.ForeignTable.NameGo}}
if err = boil.BindFast(results, &resultSlice, {{.ForeignTable.Name | singular | camelCase}}TitleCases); err != nil { if err = boil.Bind(results, &resultSlice); err != nil {
return errors.Wrap(err, "failed to bind eager loaded slice {{.ForeignTable.NameGo}}") return errors.Wrap(err, "failed to bind eager loaded slice {{.ForeignTable.NameGo}}")
} }

View file

@ -78,7 +78,7 @@ func ({{$varNameSingular}}L) Load{{$txt.Function.Name}}(e boil.Executor, singula
return errors.Wrap(err, "failed to plebian-bind eager loaded slice {{.ForeignTable}}") return errors.Wrap(err, "failed to plebian-bind eager loaded slice {{.ForeignTable}}")
} }
{{else -}} {{else -}}
if err = boil.BindFast(results, &resultSlice, {{.ForeignTable | singular | camelCase}}TitleCases); err != nil { if err = boil.Bind(results, &resultSlice); err != nil {
return errors.Wrap(err, "failed to bind eager loaded slice {{.ForeignTable}}") return errors.Wrap(err, "failed to bind eager loaded slice {{.ForeignTable}}")
} }
{{end}} {{end}}

View file

@ -34,7 +34,7 @@ func Find{{$tableNameSingular}}(exec boil.Executor, {{$pkArgs}}, selectCols ...s
q := boil.SQL(exec, query, {{$pkNames | join ", "}}) q := boil.SQL(exec, query, {{$pkNames | join ", "}})
err := q.BindFast({{$varNameSingular}}Obj, {{$varNameSingular}}TitleCases) err := q.Bind({{$varNameSingular}}Obj)
if err != nil { if err != nil {
if errors.Cause(err) == sql.ErrNoRows { if errors.Cause(err) == sql.ErrNoRows {
return nil, sql.ErrNoRows return nil, sql.ErrNoRows

View file

@ -53,10 +53,10 @@ func (o *{{$tableNameSingular}}) Insert(exec boil.Executor, whitelist ... string
{{if .UseLastInsertID}} {{if .UseLastInsertID}}
if boil.DebugMode { if boil.DebugMode {
fmt.Fprintln(boil.DebugWriter, ins) fmt.Fprintln(boil.DebugWriter, ins)
fmt.Fprintln(boil.DebugWriter, boil.GetStructValues(o, {{$varNameSingular}}TitleCases, wl...)) fmt.Fprintln(boil.DebugWriter, boil.GetStructValues(o, wl...))
} }
result, err := exec.Exec(ins, boil.GetStructValues(o, {{$varNameSingular}}TitleCases, wl...)...) result, err := exec.Exec(ins, boil.GetStructValues(o, wl...)...)
if err != nil { if err != nil {
return errors.Wrap(err, "{{.PkgName}}: unable to insert into {{.Table.Name}}") return errors.Wrap(err, "{{.PkgName}}: unable to insert into {{.Table.Name}}")
} }
@ -77,21 +77,21 @@ func (o *{{$tableNameSingular}}) Insert(exec boil.Executor, whitelist ... string
} }
sel := fmt.Sprintf(`SELECT %s FROM {{.Table.Name}} WHERE %s`, strings.Join(returnColumns, `","`), strmangle.WhereClause(1, {{$varNameSingular}}AutoIncPrimaryKeys)) sel := fmt.Sprintf(`SELECT %s FROM {{.Table.Name}} WHERE %s`, strings.Join(returnColumns, `","`), strmangle.WhereClause(1, {{$varNameSingular}}AutoIncPrimaryKeys))
err = exec.QueryRow(sel, lastID).Scan(boil.GetStructPointers(o, {{$varNameSingular}}TitleCases, returnColumns...)) err = exec.QueryRow(sel, lastID).Scan(boil.GetStructPointers(o, returnColumns...))
if err != nil { if err != nil {
return errors.Wrap(err, "{{.PkgName}}: unable to populate default values for {{.Table.Name}}") return errors.Wrap(err, "{{.PkgName}}: unable to populate default values for {{.Table.Name}}")
} }
{{else}} {{else}}
if len(returnColumns) != 0 { if len(returnColumns) != 0 {
ins = ins + fmt.Sprintf(` RETURNING %s`, strings.Join(returnColumns, ",")) ins = ins + fmt.Sprintf(` RETURNING %s`, strings.Join(returnColumns, ","))
err = exec.QueryRow(ins, boil.GetStructValues(o, {{$varNameSingular}}TitleCases, wl...)...).Scan(boil.GetStructPointers(o, {{$varNameSingular}}TitleCases, returnColumns...)...) err = exec.QueryRow(ins, boil.GetStructValues(o, wl...)...).Scan(boil.GetStructPointers(o, returnColumns...)...)
} else { } else {
_, err = exec.Exec(ins, boil.GetStructValues(o, {{$varNameSingular}}TitleCases, wl...)...) _, err = exec.Exec(ins, boil.GetStructValues(o, wl...)...)
} }
if boil.DebugMode { if boil.DebugMode {
fmt.Fprintln(boil.DebugWriter, ins) fmt.Fprintln(boil.DebugWriter, ins)
fmt.Fprintln(boil.DebugWriter, boil.GetStructValues(o, {{$varNameSingular}}TitleCases, wl...)) fmt.Fprintln(boil.DebugWriter, boil.GetStructValues(o, wl...))
} }
if err != nil { if err != nil {

View file

@ -53,7 +53,7 @@ func (o *{{$tableNameSingular}}) Update(exec boil.Executor, whitelist ... string
} }
query = fmt.Sprintf(`UPDATE {{.Table.Name}} SET %s WHERE %s`, strmangle.SetParamNames(wl), strmangle.WhereClause(len(wl)+1, {{$varNameSingular}}PrimaryKeyColumns)) query = fmt.Sprintf(`UPDATE {{.Table.Name}} SET %s WHERE %s`, strmangle.SetParamNames(wl), strmangle.WhereClause(len(wl)+1, {{$varNameSingular}}PrimaryKeyColumns))
values = boil.GetStructValues(o, {{$varNameSingular}}TitleCases, wl...) values = boil.GetStructValues(o, wl...)
values = append(values, {{.Table.PKey.Columns | stringMap .StringFuncs.titleCase | prefixStringSlice "o." | join ", "}}) values = append(values, {{.Table.PKey.Columns | stringMap .StringFuncs.titleCase | prefixStringSlice "o." | join ", "}})
if boil.DebugMode { if boil.DebugMode {

View file

@ -58,16 +58,16 @@ func (o *{{$tableNameSingular}}) Upsert(exec boil.Executor, updateOnConflict boo
if boil.DebugMode { if boil.DebugMode {
fmt.Fprintln(boil.DebugWriter, query) fmt.Fprintln(boil.DebugWriter, query)
fmt.Fprintln(boil.DebugWriter, boil.GetStructValues(o, {{$varNameSingular}}TitleCases, whitelist...)) fmt.Fprintln(boil.DebugWriter, boil.GetStructValues(o, whitelist...))
} }
{{- if .UseLastInsertID}} {{- if .UseLastInsertID}}
return errors.New("don't know how to do this yet") return errors.New("don't know how to do this yet")
{{- else}} {{- else}}
if len(ret) != 0 { if len(ret) != 0 {
err = exec.QueryRow(query, boil.GetStructValues(o, {{$varNameSingular}}TitleCases, whitelist...)...).Scan(boil.GetStructPointers(o, {{$varNameSingular}}TitleCases, ret...)...) err = exec.QueryRow(query, boil.GetStructValues(o, whitelist...)...).Scan(boil.GetStructPointers(o, ret...)...)
} else { } else {
_, err = exec.Exec(query, boil.GetStructValues(o, {{$varNameSingular}}TitleCases, whitelist...)...) _, err = exec.Exec(query, boil.GetStructValues(o, whitelist...)...)
} }
{{- end}} {{- end}}

View file

@ -74,7 +74,7 @@ func (o *{{$tableNameSingular}}Slice) ReloadAll(exec boil.Executor) error {
q := boil.SQL(exec, sql, args...) q := boil.SQL(exec, sql, args...)
err := q.BindFast(&{{$varNamePlural}}, {{$varNameSingular}}TitleCases) err := q.Bind(&{{$varNamePlural}})
if err != nil { if err != nil {
return errors.Wrap(err, "{{.PkgName}}: unable to reload all in {{$tableNameSingular}}Slice") return errors.Wrap(err, "{{.PkgName}}: unable to reload all in {{$tableNameSingular}}Slice")
} }