Finish upsert, most of upsert tests
* Fix Upsert hooks * Rename singleton template files
This commit is contained in:
parent
8dbf322754
commit
8a3a08baa0
9 changed files with 176 additions and 64 deletions
|
@ -150,11 +150,12 @@ var defaultTemplateImports = imports{
|
|||
thirdParty: importList{
|
||||
`"github.com/nullbio/sqlboiler/boil"`,
|
||||
`"github.com/nullbio/sqlboiler/boil/qm"`,
|
||||
`"github.com/nullbio/sqlboiler/strmangle"`,
|
||||
},
|
||||
}
|
||||
|
||||
var defaultSingletonTemplateImports = map[string]imports{
|
||||
"helpers": imports{
|
||||
"boil_helpers": imports{
|
||||
standard: importList{},
|
||||
thirdParty: importList{
|
||||
`"github.com/nullbio/sqlboiler/boil"`,
|
||||
|
@ -178,7 +179,7 @@ var defaultTestTemplateImports = imports{
|
|||
}
|
||||
|
||||
var defaultSingletonTestTemplateImports = map[string]imports{
|
||||
"main_helper_funcs": imports{
|
||||
"boil_main_helpers": imports{
|
||||
standard: importList{
|
||||
`"database/sql"`,
|
||||
`"os"`,
|
||||
|
@ -188,7 +189,7 @@ var defaultSingletonTestTemplateImports = map[string]imports{
|
|||
`"github.com/spf13/viper"`,
|
||||
},
|
||||
},
|
||||
"helper_funcs": imports{
|
||||
"boil_helpers": imports{
|
||||
standard: importList{
|
||||
`"crypto/md5"`,
|
||||
`"fmt"`,
|
||||
|
|
|
@ -2,8 +2,10 @@
|
|||
{{- $varNameSingular := .Table.Name | singular | camelCase -}}
|
||||
var {{$varNameSingular}}BeforeCreateHooks []{{$tableNameSingular}}Hook
|
||||
var {{$varNameSingular}}BeforeUpdateHooks []{{$tableNameSingular}}Hook
|
||||
var {{$varNameSingular}}BeforeUpsertHooks []{{$tableNameSingular}}Hook
|
||||
var {{$varNameSingular}}AfterCreateHooks []{{$tableNameSingular}}Hook
|
||||
var {{$varNameSingular}}AfterUpdateHooks []{{$tableNameSingular}}Hook
|
||||
var {{$varNameSingular}}AfterUpsertHooks []{{$tableNameSingular}}Hook
|
||||
|
||||
// doBeforeCreateHooks executes all "before create" hooks.
|
||||
func (o *{{$tableNameSingular}}) doBeforeCreateHooks() (err error) {
|
||||
|
|
|
@ -21,7 +21,7 @@ func (o *{{$tableNameSingular}}) UpdateGP(whitelist ...string) {
|
|||
// UpdateP uses an executor to update the {{$tableNameSingular}}, and panics on error.
|
||||
// See Update for whitelist behavior description.
|
||||
func (o *{{$tableNameSingular}}) UpdateP(exec boil.Executor, whitelist ... string) {
|
||||
err := o.UpdateAt(exec, {{.Table.PKey.Columns | stringMap .StringFuncs.titleCase | prefixStringSlice "o." | join ", "}}, whitelist...)
|
||||
err := o.Update(exec, whitelist...)
|
||||
if err != nil {
|
||||
panic(boil.WrapErr(err))
|
||||
}
|
||||
|
@ -33,23 +33,6 @@ func (o *{{$tableNameSingular}}) UpdateP(exec boil.Executor, whitelist ... strin
|
|||
// - All columns are inferred to start with
|
||||
// - All primary keys are subtracted from this set
|
||||
func (o *{{$tableNameSingular}}) Update(exec boil.Executor, whitelist ... string) error {
|
||||
return o.UpdateAt(exec, {{.Table.PKey.Columns | stringMap .StringFuncs.titleCase | prefixStringSlice "o." | join ", "}}, whitelist...)
|
||||
}
|
||||
|
||||
// UpdateAtG updates the {{$tableNameSingular}} using the primary key to find the row to update.
|
||||
func (o *{{$tableNameSingular}}) UpdateAtG({{$pkArgs}}, whitelist ...string) error {
|
||||
return o.UpdateAt(boil.GetDB(), {{$pkNames | join ", "}}, whitelist...)
|
||||
}
|
||||
|
||||
// UpdateAtGP updates the {{$tableNameSingular}} using the primary key to find the row to update. Panics on error.
|
||||
func (o *{{$tableNameSingular}}) UpdateAtGP({{$pkArgs}}, whitelist ...string) {
|
||||
if err := o.UpdateAt(boil.GetDB(), {{$pkNames | join ", "}}, whitelist...); err != nil {
|
||||
panic(boil.WrapErr(err))
|
||||
}
|
||||
}
|
||||
|
||||
// UpdateAt uses an executor to update the {{$tableNameSingular}} using the primary key to find the row to update.
|
||||
func (o *{{$tableNameSingular}}) UpdateAt(exec boil.Executor, {{$pkArgs}}, whitelist ...string) error {
|
||||
if err := o.doBeforeUpdateHooks(); err != nil {
|
||||
return err
|
||||
}
|
||||
|
@ -85,14 +68,6 @@ func (o *{{$tableNameSingular}}) UpdateAt(exec boil.Executor, {{$pkArgs}}, white
|
|||
return nil
|
||||
}
|
||||
|
||||
// UpdateAtP uses an executor to update the {{$tableNameSingular}} using the primary key to find the row to update.
|
||||
// Panics on error.
|
||||
func (o *{{$tableNameSingular}}) UpdateAtP(exec boil.Executor, {{$pkArgs}}, whitelist ...string) {
|
||||
if err := o.UpdateAt(exec, {{$pkNames | join ", "}}, whitelist...); err != nil {
|
||||
panic(boil.WrapErr(err))
|
||||
}
|
||||
}
|
||||
|
||||
// UpdateAll updates all rows with matching column names.
|
||||
func (q {{$varNameSingular}}Query) UpdateAll(cols M) error {
|
||||
boil.SetUpdate(q.Query, cols)
|
||||
|
|
|
@ -12,51 +12,36 @@ func (o *{{$tableNameSingular}}) UpsertGP(update bool, conflictColumns []string,
|
|||
}
|
||||
}
|
||||
|
||||
// UpsertP attempts an insert using an executor, and does an update or ignore on conflict.
|
||||
// UpsertP panics on error.
|
||||
func (o *{{$tableNameSingular}}) UpsertP(exec boil.Executor, update bool, conflictColumns []string, updateColumns []string, whitelist ...string) {
|
||||
if err := o.Upsert(exec, update, conflictColumns, updateColumns, whitelist...); err != nil {
|
||||
panic(boil.WrapErr(err))
|
||||
}
|
||||
}
|
||||
|
||||
// Upsert attempts an insert using an executor, and does an update or ignore on conflict.
|
||||
func (o *{{$tableNameSingular}}) Upsert(exec boil.Executor, update bool, conflictColumns []string, updateColumns []string, whitelist ...string) error {
|
||||
if o == nil {
|
||||
return errors.New("{{.PkgName}}: no {{.Table.Name}} provided for upsert")
|
||||
}
|
||||
|
||||
wl, returnColumns := o.generateInsertColumns(whitelist...)
|
||||
|
||||
conflict := make([]string, len(conflictColumns))
|
||||
update := make([]string, len(updateColumns))
|
||||
|
||||
copy(conflict, conflictColumns)
|
||||
copy(update, updateColumns)
|
||||
|
||||
for i, v := range conflict {
|
||||
conflict[i] = strmangle.IdentQuote(v)
|
||||
}
|
||||
|
||||
for i, v := range update {
|
||||
update[i] = strmangle.IdentQuote(v)
|
||||
}
|
||||
columns := o.generateUpsertColumns(conflictColumns, updateColumns, whitelist)
|
||||
query := o.generateUpsertQuery(update, columns)
|
||||
|
||||
var err error
|
||||
if err := o.doBeforeUpsertHooks(); err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
ins := fmt.Sprintf(`INSERT INTO {{.Table.Name}} ("%s") VALUES (%s) ON CONFLICT `, strings.Join(wl, `","`), boil.GenerateParamFlags(len(wl), 1))
|
||||
if !update {
|
||||
ins := ins + "DO NOTHING"
|
||||
} else if len(conflict) != 0 {
|
||||
ins := ins + fmt.Sprintf(`("%s") DO UPDATE SET %s`, strings.Join(conflict, `","`))
|
||||
if len(columns.returning) != 0 {
|
||||
err = exec.QueryRow(query, boil.GetStructValues(o, columns.whitelist...)...).Scan(boil.GetStructPointers(o, columns.returning...)...)
|
||||
} else {
|
||||
ins := ins + fmt.Sprintf(`("%s") DO UPDATE SET %s`, strings.Join({{$varNameSingular}}PrimaryKeyColumns, `","`))
|
||||
}
|
||||
|
||||
if len(returnColumns) != 0 {
|
||||
ins = ins + fmt.Sprintf(` RETURNING %s`, strings.Join(returnColumns, ","))
|
||||
err = exec.QueryRow(ins, boil.GetStructValues(o, wl...)...).Scan(boil.GetStructPointers(o, returnColumns...)...)
|
||||
} else {
|
||||
_, err = exec.Exec(ins, {{.Table.Columns | columnNames | stringMap .StringFuncs.titleCase | prefixStringSlice "o." | join ", "}})
|
||||
_, err = exec.Exec(query, {{.Table.Columns | columnNames | stringMap .StringFuncs.titleCase | prefixStringSlice "o." | join ", "}})
|
||||
}
|
||||
|
||||
if boil.DebugMode {
|
||||
fmt.Fprintln(boil.DebugWriter, ins, boil.GetStructValues(o, wl...))
|
||||
fmt.Fprintln(boil.DebugWriter, query, boil.GetStructValues(o, columns.whitelist...))
|
||||
}
|
||||
|
||||
if err != nil {
|
||||
|
@ -70,10 +55,70 @@ func (o *{{$tableNameSingular}}) Upsert(exec boil.Executor, update bool, conflic
|
|||
return nil
|
||||
}
|
||||
|
||||
// UpsertP attempts an insert using an executor, and does an update or ignore on conflict.
|
||||
// UpsertP panics on error.
|
||||
func (o *{{$tableNameSingular}}) UpsertP(exec boil.Executor, update bool, conflictColumns []string, updateColumns []string, whitelist ...string) {
|
||||
if err := o.Upsert(exec, update, conflictColumns, updateColumns, whitelist...); err != nil {
|
||||
panic(boil.WrapErr(err))
|
||||
}
|
||||
// generateUpsertColumns builds an upsertData object, using generated values when necessary.
|
||||
func (o *{{$tableNameSingular}}) generateUpsertColumns(conflict []string, update []string, whitelist []string) upsertData {
|
||||
var upsertCols upsertData
|
||||
|
||||
upsertCols.whitelist, upsertCols.returning = o.generateInsertColumns(whitelist...)
|
||||
|
||||
upsertCols.conflict = make([]string, len(conflict))
|
||||
upsertCols.update = make([]string, len(update))
|
||||
|
||||
// generates the ON CONFLICT() columns if none are provided
|
||||
upsertCols.conflict = o.generateConflictColumns(conflict...)
|
||||
|
||||
// generate the UPDATE SET columns if none are provided
|
||||
upsertCols.update = o.generateUpdateColumns(update...)
|
||||
|
||||
return upsertCols
|
||||
}
|
||||
|
||||
// generateConflictColumns returns the user provided columns.
|
||||
// If no columns are provided, it returns the primary key columns.
|
||||
func (o *{{$tableNameSingular}}) generateConflictColumns(columns ...string) []string {
|
||||
if len(columns) != 0 {
|
||||
return columns
|
||||
}
|
||||
|
||||
c := make([]string, len({{$varNameSingular}}PrimaryKeyColumns))
|
||||
copy(c, {{$varNameSingular}}PrimaryKeyColumns)
|
||||
|
||||
return c
|
||||
}
|
||||
|
||||
// generateUpsertQuery builds a SQL statement string using the upsertData provided.
|
||||
func (o *{{$tableNameSingular}}) generateUpsertQuery(update bool, columns upsertData) string {
|
||||
var set, query string
|
||||
|
||||
for i, v := range columns.conflict {
|
||||
columns.conflict[i] = strmangle.IdentQuote(v)
|
||||
}
|
||||
|
||||
var sets []string
|
||||
// Generate the UPDATE SET clause
|
||||
for _, v := range columns.update {
|
||||
quoted := strmangle.IdentQuote(v)
|
||||
sets = append(sets, fmt.Sprintf("%s = EXCLUDED.%s", quoted, quoted))
|
||||
}
|
||||
set = strings.Join(sets, ", ")
|
||||
|
||||
query = fmt.Sprintf(
|
||||
`INSERT INTO {{.Table.Name}} ("%s") VALUES (%s) ON CONFLICT DO`,
|
||||
strings.Join(columns.whitelist, `","`),
|
||||
boil.GenerateParamFlags(len(columns.whitelist), 1),
|
||||
)
|
||||
|
||||
if !update {
|
||||
query = query + " NOTHING"
|
||||
} else if len(columns.conflict) != 0 {
|
||||
query = fmt.Sprintf(`%s("%s") UPDATE SET %s`, query, strings.Join(columns.conflict, `","`), set)
|
||||
} else {
|
||||
query = fmt.Sprintf(`%s("%s") UPDATE SET %s`, query, strings.Join({{$varNameSingular}}PrimaryKeyColumns, `","`), set)
|
||||
}
|
||||
|
||||
if len(columns.returning) != 0 {
|
||||
query = fmt.Sprintf(`%s RETURNING %s`, query, strings.Join(columns.returning, ","))
|
||||
}
|
||||
|
||||
return query
|
||||
}
|
||||
|
|
6
templates/singleton/boil_types.tpl
Normal file
6
templates/singleton/boil_types.tpl
Normal file
|
@ -0,0 +1,6 @@
|
|||
type upsertData struct {
|
||||
conflict []string
|
||||
update []string
|
||||
whitelist []string
|
||||
returning []string
|
||||
}
|
|
@ -0,0 +1,83 @@
|
|||
{{- $tableNameSingular := .Table.Name | singular | titleCase -}}
|
||||
{{- $dbName := singular .Table.Name -}}
|
||||
{{- $tableNamePlural := .Table.Name | plural | titleCase -}}
|
||||
{{- $varNamePlural := .Table.Name | plural | camelCase -}}
|
||||
{{- $varNameSingular := .Table.Name | singular | camelCase -}}
|
||||
{{- $parent := . -}}
|
||||
func Test{{$tableNamePlural}}Upsert(t *testing.T) {
|
||||
//var err error
|
||||
|
||||
o := {{$tableNameSingular}}{}
|
||||
|
||||
columns := o.generateUpsertColumns([]string{"one", "two"}, []string{"three", "four"}, []string{"five", "six"})
|
||||
if columns.conflict[0] != "one" || columns.conflict[1] != "two" {
|
||||
t.Errorf("Expected conflict to be %v, got %v", []string{"one", "two"}, columns.conflict)
|
||||
}
|
||||
|
||||
if columns.update[0] != "three" || columns.update[1] != "four" {
|
||||
t.Errorf("Expected update to be %v, got %v", []string{"three", "four"}, columns.update)
|
||||
}
|
||||
|
||||
if columns.whitelist[0] != "five" || columns.whitelist[1] != "six" {
|
||||
t.Errorf("Expected whitelist to be %v, got %v", []string{"five", "six"}, columns.whitelist)
|
||||
}
|
||||
|
||||
columns = o.generateUpsertColumns(nil, nil, nil)
|
||||
if len(columns.whitelist) == 0 {
|
||||
t.Errorf("Expected whitelist to contain columns, but got len 0")
|
||||
}
|
||||
|
||||
if len(columns.conflict) == 0 {
|
||||
t.Errorf("Expected conflict to contain columns, but got len 0")
|
||||
}
|
||||
|
||||
if len(columns.update) == 0 {
|
||||
t.Errorf("expected update to contain columns, but got len 0")
|
||||
}
|
||||
|
||||
upsertCols := upsertData{
|
||||
conflict: []string{},
|
||||
update: []string{},
|
||||
whitelist: []string{"thing"},
|
||||
returning: []string{},
|
||||
}
|
||||
|
||||
query := o.generateUpsertQuery(false, upsertCols)
|
||||
expectedQuery := `INSERT INTO {{.Table.Name}} ("thing") VALUES ($1) ON CONFLICT DO NOTHING`
|
||||
|
||||
if query != expectedQuery {
|
||||
t.Errorf("Expected query mismatch:\n\n%s\n%s\n", query, expectedQuery)
|
||||
}
|
||||
|
||||
/*
|
||||
query = o.generateUpsertQuery(true, upsertCols)
|
||||
primKeys := strings.Join(strmangle.IdentQuote())
|
||||
expectedQuery = `INSERT INTO {{.Table.Name}} ("thing") VALUES ($1) ON CONFLICT DO UPDATE()`
|
||||
|
||||
if query != expectedQuery {
|
||||
t.Errorf("Expected query mismatch:\n\n%s\n%s\n", query, expectedQuery)
|
||||
}
|
||||
*/
|
||||
|
||||
/*
|
||||
create empty row
|
||||
assign random values to it
|
||||
|
||||
attempt to insert it using upsert
|
||||
make sure values come back appropriately
|
||||
|
||||
attempt to upsert row again, make sure comes back as prim key error
|
||||
attempt upsert again, set update to false, ensure it ignores error
|
||||
|
||||
attempt to randomize everything except primary keys on duplicate row
|
||||
attempt upsert again, set update to true, nil, nil
|
||||
perform a find on the the row
|
||||
check if the found row matches the upsert object to ensure returning cols worked appropriately and update worked appropriately
|
||||
|
||||
|
||||
|
||||
|
||||
*/
|
||||
|
||||
{{$varNamePlural}}DeleteAllRows(t)
|
||||
}
|
Loading…
Add table
Reference in a new issue