Boilerplate for upsert

This commit is contained in:
Patrick O'brien 2016-08-04 13:11:45 +10:00
parent 358dac851f
commit 70b2dcfbb2
7 changed files with 108 additions and 29 deletions

View file

@ -7,6 +7,8 @@ type HookPoint int
const (
HookAfterCreate HookPoint = iota + 1
HookAfterUpdate
HookAfterUpsert
HookBeforeCreate
HookBeforeUpdate
HookBeforeUpsert
)

View file

@ -52,8 +52,6 @@ func buildQuery(q *Query) (string, []interface{}) {
return q.plainSQL.sql, q.plainSQL.args
case q.delete:
buf, args = buildDeleteQuery(q)
case len(q.update) > 0:
buf, args = buildUpdateQuery(q)
default:
buf, args = buildSelectQuery(q)
}
@ -115,13 +113,6 @@ func buildDeleteQuery(q *Query) (*bytes.Buffer, []interface{}) {
return buf, args
}
func buildUpdateQuery(q *Query) (*bytes.Buffer, []interface{}) {
buf := &bytes.Buffer{}
buf.WriteByte(';')
return buf, nil
}
// ExecQuery executes a query that does not need a row returned
func ExecQuery(q *Query) (sql.Result, error) {
qs, args := buildQuery(q)
@ -164,11 +155,6 @@ func SetDelete(q *Query) {
q.delete = true
}
// SetUpdate on the query.
func SetUpdate(q *Query, cols map[string]interface{}) {
q.update = cols
}
// SetExecutor on the query.
func SetExecutor(q *Query, exec Executor) {
q.executor = exec

View file

@ -258,21 +258,6 @@ func TestSetDelete(t *testing.T) {
}
}
func TestSetUpdate(t *testing.T) {
t.Parallel()
q := &Query{}
SetUpdate(q, map[string]interface{}{"col1": 1, "col2": 2})
if len(q.update) != 2 {
t.Errorf("Expected len 2, got %d", len(q.update))
}
if q.update["col1"] != 1 && q.update["col2"] != 2 {
t.Errorf("Value misatch: %#v", q.update)
}
}
func TestSetExecutor(t *testing.T) {
t.Parallel()

View file

@ -202,6 +202,7 @@ func RandomizeStruct(str interface{}, colTypes map[string]string, includeInvalid
return nil
}
// RandomizeValidatedStruct takes an object fills its validated columns with random data.
func RandomizeValidatedStruct(obj interface{}, validatedCols []string, colTypes map[string]string) error {
// Check if it's pointer
value := reflect.ValueOf(obj)

View file

@ -27,6 +27,17 @@ func (o *{{$tableNameSingular}}) doBeforeUpdateHooks() (err error) {
return nil
}
// doBeforeUpsertHooks executes all "before Upsert" hooks.
func (o *{{$tableNameSingular}}) doBeforeUpsertHooks() (err error) {
for _, hook := range {{$varNameSingular}}BeforeUpsertHooks {
if err := hook(o); err != nil {
return err
}
}
return nil
}
// doAfterCreateHooks executes all "after create" hooks.
func (o *{{$tableNameSingular}}) doAfterCreateHooks() (err error) {
for _, hook := range {{$varNameSingular}}AfterCreateHooks {
@ -49,15 +60,30 @@ func (o *{{$tableNameSingular}}) doAfterUpdateHooks() (err error) {
return nil
}
// doAfterUpsertHooks executes all "after Upsert" hooks.
func (o *{{$tableNameSingular}}) doAfterUpsertHooks() (err error) {
for _, hook := range {{$varNameSingular}}AfterUpsertHooks {
if err := hook(o); err != nil {
return err
}
}
return nil
}
func {{$tableNameSingular}}AddHook(hookPoint boil.HookPoint, {{$varNameSingular}}Hook {{$tableNameSingular}}Hook) {
switch hookPoint {
case boil.HookBeforeCreate:
{{$varNameSingular}}BeforeCreateHooks = append({{$varNameSingular}}BeforeCreateHooks, {{$varNameSingular}}Hook)
case boil.HookBeforeUpdate:
{{$varNameSingular}}BeforeUpdateHooks = append({{$varNameSingular}}BeforeUpdateHooks, {{$varNameSingular}}Hook)
case boil.HookBeforeUpsert:
{{$varNameSingular}}BeforeUpsertHooks = append({{$varNameSingular}}BeforeUpsertHooks, {{$varNameSingular}}Hook)
case boil.HookAfterCreate:
{{$varNameSingular}}AfterCreateHooks = append({{$varNameSingular}}AfterCreateHooks, {{$varNameSingular}}Hook)
case boil.HookAfterUpdate:
{{$varNameSingular}}AfterUpdateHooks = append({{$varNameSingular}}AfterUpdateHooks, {{$varNameSingular}}Hook)
case boil.HookAfterUpsert:
{{$varNameSingular}}AfterUpsertHooks = append({{$varNameSingular}}AfterUpsertHooks, {{$varNameSingular}}Hook)
}
}

View file

@ -0,0 +1,79 @@
{{- $tableNameSingular := .Table.Name | singular | titleCase -}}
{{- $varNameSingular := .Table.Name | singular | camelCase -}}
// UpsertG attempts an insert, and does an update or ignore on conflict.
func (o *{{$tableNameSingular}}) UpsertG(update bool, conflictColumns []string, updateColumns []string, whitelist ...string) error {
return o.Upsert(boil.GetDB(), update, conflictColumns, updateColumns, whitelist...)
}
// UpsertGP attempts an insert, and does an update or ignore on conflict. Panics on error.
func (o *{{$tableNameSingular}}) UpsertGP(update bool, conflictColumns []string, updateColumns []string, whitelist ...string) {
if err := o.Upsert(boil.GetDB(), 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)
}
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, `","`))
} 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 ", "}})
}
if boil.DebugMode {
fmt.Fprintln(boil.DebugWriter, ins, boil.GetStructValues(o, wl...))
}
if err != nil {
return fmt.Errorf("{{.PkgName}}: unable to upsert for {{.Table.Name}}: %s", err)
}
if err := o.doAfterUpsertHooks(); err != nil {
return err
}
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))
}
}

View file