MSSQL Upsert statement
This commit is contained in:
parent
6bc6b1690c
commit
9bafa2f158
7 changed files with 89 additions and 41 deletions
|
@ -283,6 +283,49 @@ func BuildUpsertQueryPostgres(dia Dialect, tableName string, updateOnConflict bo
|
|||
return buf.String()
|
||||
}
|
||||
|
||||
// BuildUpsertQueryMSSQL builds a SQL statement string using the upsertData provided.
|
||||
func BuildUpsertQueryMSSQL(dia Dialect, tableName string, primary, update, insert []string, output []string) string {
|
||||
insert = strmangle.IdentQuoteSlice(dia.LQ, dia.RQ, insert)
|
||||
|
||||
buf := strmangle.GetBuffer()
|
||||
defer strmangle.PutBuffer(buf)
|
||||
|
||||
startIndex := 1
|
||||
|
||||
fmt.Fprintf(buf, "MERGE INTO %s as [t]\n", tableName)
|
||||
fmt.Fprintf(buf, "USING (SELECT %s) as [s] ([%s])\n",
|
||||
strmangle.Placeholders(dia.IndexPlaceholders, len(primary), startIndex, 1),
|
||||
strings.Join(primary, string(dia.RQ)+","+string(dia.LQ)))
|
||||
fmt.Fprint(buf, "ON (")
|
||||
for i, v := range primary {
|
||||
if i != 0 {
|
||||
fmt.Fprint(buf, " AND ")
|
||||
}
|
||||
fmt.Fprintf(buf, "[s].[%s] = [t].[%s]", v, v)
|
||||
}
|
||||
fmt.Fprint(buf, ")\n")
|
||||
|
||||
startIndex = len(primary) + 1
|
||||
|
||||
fmt.Fprint(buf, "WHEN MATCHED THEN ")
|
||||
fmt.Fprintf(buf, "UPDATE SET %s\n", strmangle.SetParamNames(string(dia.LQ), string(dia.RQ), startIndex, update))
|
||||
|
||||
startIndex = len(primary) + 1 + len(update)
|
||||
|
||||
fmt.Fprint(buf, "WHEN NOT MATCHED THEN ")
|
||||
fmt.Fprintf(buf, "INSERT (%s) VALUES (%s)",
|
||||
strings.Join(insert, ", "),
|
||||
strmangle.Placeholders(dia.IndexPlaceholders, len(insert), startIndex, 1))
|
||||
|
||||
if len(output) > 0 {
|
||||
fmt.Fprintf(buf, "\nOUTPUT INSERTED.[%s];", strings.Join(output, "],INSERTED.["))
|
||||
} else {
|
||||
fmt.Fprint(buf, ";")
|
||||
}
|
||||
|
||||
return buf.String()
|
||||
}
|
||||
|
||||
func writeModifiers(q *Query, buf *bytes.Buffer, args *[]interface{}) {
|
||||
if len(q.groupBy) != 0 {
|
||||
fmt.Fprintf(buf, " GROUP BY %s", strings.Join(q.groupBy, ", "))
|
||||
|
|
|
@ -35,10 +35,8 @@ var (
|
|||
{{$varNameSingular}}InsertCache = make(map[string]insertCache)
|
||||
{{$varNameSingular}}UpdateCacheMut sync.RWMutex
|
||||
{{$varNameSingular}}UpdateCache = make(map[string]updateCache)
|
||||
{{if ne .DriverName "mssql"}}
|
||||
{{$varNameSingular}}UpsertCacheMut sync.RWMutex
|
||||
{{$varNameSingular}}UpsertCache = make(map[string]insertCache)
|
||||
{{end}}
|
||||
)
|
||||
|
||||
var (
|
||||
|
|
|
@ -4,17 +4,13 @@
|
|||
var {{$varNameSingular}}BeforeInsertHooks []{{$tableNameSingular}}Hook
|
||||
var {{$varNameSingular}}BeforeUpdateHooks []{{$tableNameSingular}}Hook
|
||||
var {{$varNameSingular}}BeforeDeleteHooks []{{$tableNameSingular}}Hook
|
||||
{{if ne .DriverName "mssql" -}}
|
||||
var {{$varNameSingular}}BeforeUpsertHooks []{{$tableNameSingular}}Hook
|
||||
{{- end}}
|
||||
|
||||
var {{$varNameSingular}}AfterInsertHooks []{{$tableNameSingular}}Hook
|
||||
var {{$varNameSingular}}AfterSelectHooks []{{$tableNameSingular}}Hook
|
||||
var {{$varNameSingular}}AfterUpdateHooks []{{$tableNameSingular}}Hook
|
||||
var {{$varNameSingular}}AfterDeleteHooks []{{$tableNameSingular}}Hook
|
||||
{{if ne .DriverName "mssql" -}}
|
||||
var {{$varNameSingular}}AfterUpsertHooks []{{$tableNameSingular}}Hook
|
||||
{{- end}}
|
||||
|
||||
// doBeforeInsertHooks executes all "before insert" hooks.
|
||||
func (o *{{$tableNameSingular}}) doBeforeInsertHooks(exec boil.Executor) (err error) {
|
||||
|
@ -49,7 +45,6 @@ func (o *{{$tableNameSingular}}) doBeforeDeleteHooks(exec boil.Executor) (err er
|
|||
return nil
|
||||
}
|
||||
|
||||
{{- if ne .DriverName "mssql" -}}
|
||||
// doBeforeUpsertHooks executes all "before Upsert" hooks.
|
||||
func (o *{{$tableNameSingular}}) doBeforeUpsertHooks(exec boil.Executor) (err error) {
|
||||
for _, hook := range {{$varNameSingular}}BeforeUpsertHooks {
|
||||
|
@ -60,7 +55,6 @@ func (o *{{$tableNameSingular}}) doBeforeUpsertHooks(exec boil.Executor) (err er
|
|||
|
||||
return nil
|
||||
}
|
||||
{{- end}}
|
||||
|
||||
// doAfterInsertHooks executes all "after Insert" hooks.
|
||||
func (o *{{$tableNameSingular}}) doAfterInsertHooks(exec boil.Executor) (err error) {
|
||||
|
@ -106,7 +100,6 @@ func (o *{{$tableNameSingular}}) doAfterDeleteHooks(exec boil.Executor) (err err
|
|||
return nil
|
||||
}
|
||||
|
||||
{{- if ne .DriverName "mssql" -}}
|
||||
// doAfterUpsertHooks executes all "after Upsert" hooks.
|
||||
func (o *{{$tableNameSingular}}) doAfterUpsertHooks(exec boil.Executor) (err error) {
|
||||
for _, hook := range {{$varNameSingular}}AfterUpsertHooks {
|
||||
|
@ -117,7 +110,6 @@ func (o *{{$tableNameSingular}}) doAfterUpsertHooks(exec boil.Executor) (err err
|
|||
|
||||
return nil
|
||||
}
|
||||
{{- end}}
|
||||
|
||||
// Add{{$tableNameSingular}}Hook registers your hook function for all future operations.
|
||||
func Add{{$tableNameSingular}}Hook(hookPoint boil.HookPoint, {{$varNameSingular}}Hook {{$tableNameSingular}}Hook) {
|
||||
|
@ -128,10 +120,8 @@ func Add{{$tableNameSingular}}Hook(hookPoint boil.HookPoint, {{$varNameSingular}
|
|||
{{$varNameSingular}}BeforeUpdateHooks = append({{$varNameSingular}}BeforeUpdateHooks, {{$varNameSingular}}Hook)
|
||||
case boil.BeforeDeleteHook:
|
||||
{{$varNameSingular}}BeforeDeleteHooks = append({{$varNameSingular}}BeforeDeleteHooks, {{$varNameSingular}}Hook)
|
||||
{{if ne .DriverName "mssql" -}}
|
||||
case boil.BeforeUpsertHook:
|
||||
{{$varNameSingular}}BeforeUpsertHooks = append({{$varNameSingular}}BeforeUpsertHooks, {{$varNameSingular}}Hook)
|
||||
{{- end}}
|
||||
case boil.AfterInsertHook:
|
||||
{{$varNameSingular}}AfterInsertHooks = append({{$varNameSingular}}AfterInsertHooks, {{$varNameSingular}}Hook)
|
||||
case boil.AfterSelectHook:
|
||||
|
@ -140,10 +130,8 @@ func Add{{$tableNameSingular}}Hook(hookPoint boil.HookPoint, {{$varNameSingular}
|
|||
{{$varNameSingular}}AfterUpdateHooks = append({{$varNameSingular}}AfterUpdateHooks, {{$varNameSingular}}Hook)
|
||||
case boil.AfterDeleteHook:
|
||||
{{$varNameSingular}}AfterDeleteHooks = append({{$varNameSingular}}AfterDeleteHooks, {{$varNameSingular}}Hook)
|
||||
{{if ne .DriverName "mssql" -}}
|
||||
case boil.AfterUpsertHook:
|
||||
{{$varNameSingular}}AfterUpsertHooks = append({{$varNameSingular}}AfterUpsertHooks, {{$varNameSingular}}Hook)
|
||||
{{- end}}
|
||||
}
|
||||
}
|
||||
{{- end}}
|
||||
|
|
|
@ -1,29 +1,28 @@
|
|||
{{- if ne .DriverName "mssql" -}}
|
||||
{{- $tableNameSingular := .Table.Name | singular | titleCase -}}
|
||||
{{- $varNameSingular := .Table.Name | singular | camelCase -}}
|
||||
{{- $schemaTable := .Table.Name | .SchemaTable}}
|
||||
// UpsertG attempts an insert, and does an update or ignore on conflict.
|
||||
func (o *{{$tableNameSingular}}) UpsertG({{if ne .DriverName "mysql"}}updateOnConflict bool, conflictColumns []string, {{end}}updateColumns []string, whitelist ...string) error {
|
||||
return o.Upsert(boil.GetDB(), {{if ne .DriverName "mysql"}}updateOnConflict, conflictColumns, {{end}}updateColumns, whitelist...)
|
||||
func (o *{{$tableNameSingular}}) UpsertG({{if eq .DriverName "postgres"}}updateOnConflict bool, conflictColumns []string, {{end}}updateColumns []string, whitelist ...string) error {
|
||||
return o.Upsert(boil.GetDB(), {{if eq .DriverName "postgres"}}updateOnConflict, conflictColumns, {{end}}updateColumns, whitelist...)
|
||||
}
|
||||
|
||||
// UpsertGP attempts an insert, and does an update or ignore on conflict. Panics on error.
|
||||
func (o *{{$tableNameSingular}}) UpsertGP({{if ne .DriverName "mysql"}}updateOnConflict bool, conflictColumns []string, {{end}}updateColumns []string, whitelist ...string) {
|
||||
if err := o.Upsert(boil.GetDB(), {{if ne .DriverName "mysql"}}updateOnConflict, conflictColumns, {{end}}updateColumns, whitelist...); err != nil {
|
||||
func (o *{{$tableNameSingular}}) UpsertGP({{if eq .DriverName "postgres"}}updateOnConflict bool, conflictColumns []string, {{end}}updateColumns []string, whitelist ...string) {
|
||||
if err := o.Upsert(boil.GetDB(), {{if eq .DriverName "postgres"}}updateOnConflict, conflictColumns, {{end}}updateColumns, whitelist...); err != nil {
|
||||
panic(boil.WrapErr(err))
|
||||
}
|
||||
}
|
||||
|
||||
// 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, {{if ne .DriverName "mysql"}}updateOnConflict bool, conflictColumns []string, {{end}}updateColumns []string, whitelist ...string) {
|
||||
if err := o.Upsert(exec, {{if ne .DriverName "mysql"}}updateOnConflict, conflictColumns, {{end}}updateColumns, whitelist...); err != nil {
|
||||
func (o *{{$tableNameSingular}}) UpsertP(exec boil.Executor, {{if eq .DriverName "postgres"}}updateOnConflict bool, conflictColumns []string, {{end}}updateColumns []string, whitelist ...string) {
|
||||
if err := o.Upsert(exec, {{if eq .DriverName "postgres"}}updateOnConflict, conflictColumns, {{end}}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, {{if ne .DriverName "mysql"}}updateOnConflict bool, conflictColumns []string, {{end}}updateColumns []string, whitelist ...string) error {
|
||||
func (o *{{$tableNameSingular}}) Upsert(exec boil.Executor, {{if eq .DriverName "postgres"}}updateOnConflict bool, conflictColumns []string, {{end}}updateColumns []string, whitelist ...string) error {
|
||||
if o == nil {
|
||||
return errors.New("{{.PkgName}}: no {{.Table.Name}} provided for upsert")
|
||||
}
|
||||
|
@ -40,7 +39,7 @@ func (o *{{$tableNameSingular}}) Upsert(exec boil.Executor, {{if ne .DriverName
|
|||
|
||||
// Build cache key in-line uglily - mysql vs postgres problems
|
||||
buf := strmangle.GetBuffer()
|
||||
{{if ne .DriverName "mysql" -}}
|
||||
{{if eq .DriverName "postgres"}}
|
||||
if updateOnConflict {
|
||||
buf.WriteByte('t')
|
||||
} else {
|
||||
|
@ -73,36 +72,64 @@ func (o *{{$tableNameSingular}}) Upsert(exec boil.Executor, {{if ne .DriverName
|
|||
var err error
|
||||
|
||||
if !cached {
|
||||
var ret []string
|
||||
whitelist, ret = strmangle.InsertColumnSet(
|
||||
insert, ret := strmangle.InsertColumnSet(
|
||||
{{$varNameSingular}}Columns,
|
||||
{{$varNameSingular}}ColumnsWithDefault,
|
||||
{{$varNameSingular}}ColumnsWithoutDefault,
|
||||
nzDefaults,
|
||||
whitelist,
|
||||
)
|
||||
{{- if eq .DriverName "mssql"}}
|
||||
insert = strmangle.SetComplement(insert, {{$varNameSingular}}ColumnsWithAuto)
|
||||
for i, v := range insert {
|
||||
if strmangle.ContainsAny({{$varNameSingular}}PrimaryKeyColumns, v) && strmangle.ContainsAny({{$varNameSingular}}ColumnsWithDefault, v) {
|
||||
insert = append(insert[:i], insert[i+1:]...)
|
||||
}
|
||||
}
|
||||
if len(insert) == 0 {
|
||||
return errors.New("models: unable to upsert {{.Table.Name}}, could not build insert column list")
|
||||
}
|
||||
|
||||
ret = strmangle.SetMerge(ret, {{$varNameSingular}}ColumnsWithAuto)
|
||||
ret = strmangle.SetMerge(ret, {{$varNameSingular}}ColumnsWithDefault)
|
||||
|
||||
{{end -}}
|
||||
update := strmangle.UpdateColumnSet(
|
||||
{{$varNameSingular}}Columns,
|
||||
{{$varNameSingular}}PrimaryKeyColumns,
|
||||
updateColumns,
|
||||
)
|
||||
|
||||
{{- if eq .DriverName "mssql"}}
|
||||
update = strmangle.SetComplement(update, {{$varNameSingular}}ColumnsWithAuto)
|
||||
{{end -}}
|
||||
|
||||
if len(update) == 0 {
|
||||
return errors.New("{{.PkgName}}: unable to upsert {{.Table.Name}}, could not build update column list")
|
||||
}
|
||||
|
||||
{{if ne .DriverName "mysql" -}}
|
||||
{{if eq .DriverName "postgres"}}
|
||||
conflict := conflictColumns
|
||||
if len(conflict) == 0 {
|
||||
conflict = make([]string, len({{$varNameSingular}}PrimaryKeyColumns))
|
||||
copy(conflict, {{$varNameSingular}}PrimaryKeyColumns)
|
||||
}
|
||||
cache.query = queries.BuildUpsertQueryPostgres(dialect, "{{$schemaTable}}", updateOnConflict, ret, update, conflict, whitelist)
|
||||
{{- else -}}
|
||||
cache.query = queries.BuildUpsertQueryMySQL(dialect, "{{.Table.Name}}", update, whitelist)
|
||||
cache.query = queries.BuildUpsertQueryPostgres(dialect, "{{$schemaTable}}", updateOnConflict, ret, update, conflict, insert)
|
||||
{{- end -}}
|
||||
|
||||
{{if eq .DriverName "mysql"}}
|
||||
cache.query = queries.BuildUpsertQueryMySQL(dialect, "{{.Table.Name}}", update, insert)
|
||||
cache.retQuery = fmt.Sprintf(
|
||||
"SELECT %s FROM {{.LQ}}{{.Table.Name}}{{.RQ}} WHERE {{whereClause .LQ .RQ 0 .Table.PKey.Columns}}",
|
||||
strings.Join(strmangle.IdentQuoteSlice(dialect.LQ, dialect.RQ, ret), ","),
|
||||
)
|
||||
{{- end -}}
|
||||
|
||||
{{if eq .DriverName "mssql"}}
|
||||
cache.query = queries.BuildUpsertQueryMSSQL(dialect, "{{.Table.Name}}", {{$varNameSingular}}PrimaryKeyColumns, update, insert, ret)
|
||||
|
||||
whitelist = append({{$varNameSingular}}PrimaryKeyColumns, update...)
|
||||
whitelist = append(whitelist, insert...)
|
||||
{{- end}}
|
||||
|
||||
cache.valueMapping, err = queries.BindMapping({{$varNameSingular}}Type, {{$varNameSingular}}Mapping, whitelist)
|
||||
|
@ -207,5 +234,4 @@ CacheNoHooks:
|
|||
{{- else -}}
|
||||
return nil
|
||||
{{- end}}
|
||||
}
|
||||
{{- end}}
|
||||
}
|
|
@ -38,7 +38,6 @@ func {{$varNameSingular}}AfterDeleteHook(e boil.Executor, o *{{$tableNameSingula
|
|||
return nil
|
||||
}
|
||||
|
||||
{{if ne .DriverName "mssql" -}}
|
||||
func {{$varNameSingular}}BeforeUpsertHook(e boil.Executor, o *{{$tableNameSingular}}) error {
|
||||
*o = {{$tableNameSingular}}{}
|
||||
return nil
|
||||
|
@ -48,7 +47,6 @@ func {{$varNameSingular}}AfterUpsertHook(e boil.Executor, o *{{$tableNameSingula
|
|||
*o = {{$tableNameSingular}}{}
|
||||
return nil
|
||||
}
|
||||
{{- end}}
|
||||
|
||||
func test{{$tableNamePlural}}Hooks(t *testing.T) {
|
||||
t.Parallel()
|
||||
|
@ -126,7 +124,6 @@ func test{{$tableNamePlural}}Hooks(t *testing.T) {
|
|||
}
|
||||
{{$varNameSingular}}AfterDeleteHooks = []{{$tableNameSingular}}Hook{}
|
||||
|
||||
{{if ne .DriverName "mssql" -}}
|
||||
Add{{$tableNameSingular}}Hook(boil.BeforeUpsertHook, {{$varNameSingular}}BeforeUpsertHook)
|
||||
if err = o.doBeforeUpsertHooks(nil); err != nil {
|
||||
t.Errorf("Unable to execute doBeforeUpsertHooks: %s", err)
|
||||
|
@ -144,6 +141,5 @@ func test{{$tableNamePlural}}Hooks(t *testing.T) {
|
|||
t.Errorf("Expected AfterUpsertHook function to empty object, but got: %#v", o)
|
||||
}
|
||||
{{$varNameSingular}}AfterUpsertHooks = []{{$tableNameSingular}}Hook{}
|
||||
{{- end}}
|
||||
}
|
||||
{{- end}}
|
||||
|
|
|
@ -327,7 +327,7 @@ func TestSliceUpdateAll(t *testing.T) {
|
|||
{{end -}}
|
||||
{{- end -}}
|
||||
}
|
||||
{{if ne .DriverName "mssql" -}}
|
||||
|
||||
func TestUpsert(t *testing.T) {
|
||||
{{- range $index, $table := .Tables}}
|
||||
{{- if $table.IsJoinTable -}}
|
||||
|
@ -336,5 +336,4 @@ func TestUpsert(t *testing.T) {
|
|||
t.Run("{{$tableName}}", test{{$tableName}}Upsert)
|
||||
{{end -}}
|
||||
{{- end -}}
|
||||
}
|
||||
{{- end -}}
|
||||
}
|
|
@ -1,4 +1,3 @@
|
|||
{{- if ne .DriverName "mssql" -}}
|
||||
{{- $tableNameSingular := .Table.Name | singular | titleCase -}}
|
||||
{{- $tableNamePlural := .Table.Name | plural | titleCase -}}
|
||||
{{- $varNamePlural := .Table.Name | plural | camelCase -}}
|
||||
|
@ -48,5 +47,4 @@ func test{{$tableNamePlural}}Upsert(t *testing.T) {
|
|||
if count != 1 {
|
||||
t.Error("want one record, got:", count)
|
||||
}
|
||||
}
|
||||
{{- end}}
|
||||
}
|
Loading…
Reference in a new issue