Improve insert #1583

Merged
lunny merged 1 commits from lunny/improve_insert into master 2020-03-08 05:14:36 +00:00
4 changed files with 190 additions and 131 deletions

View File

@ -6,7 +6,6 @@ package xorm
import (
"errors"
"fmt"
)
var (
@ -23,13 +22,3 @@ var (
// ErrConditionType condition type unsupported
ErrConditionType = errors.New("Unsupported condition type")
)
// ErrFieldIsNotExist columns does not exist
type ErrFieldIsNotExist struct {
FieldName string
TableName string
}
func (e ErrFieldIsNotExist) Error() string {
return fmt.Sprintf("field %s is not valid on table %s", e.FieldName, e.TableName)
}

View File

@ -0,0 +1,143 @@
// Copyright 2020 The Xorm Authors. All rights reserved.
// Use of this source code is governed by a BSD-style
// license that can be found in the LICENSE file.
package statements
import (
"strings"
"xorm.io/builder"
"xorm.io/xorm/schemas"
)
func (statement *Statement) writeInsertOutput(buf *strings.Builder, table *schemas.Table) error {
if statement.dialect.URI().DBType == schemas.MSSQL && len(table.AutoIncrement) > 0 {
if _, err := buf.WriteString(" OUTPUT Inserted."); err != nil {
return err
}
if _, err := buf.WriteString(table.AutoIncrement); err != nil {
return err
}
}
return nil
}
func (statement *Statement) GenInsertSQL(colNames []string, args []interface{}) (string, []interface{}, error) {
var (
table = statement.RefTable
tableName = statement.TableName()
exprs = statement.ExprColumns
colPlaces = strings.Repeat("?, ", len(colNames))
)
if exprs.Len() <= 0 && len(colPlaces) > 0 {
colPlaces = colPlaces[0 : len(colPlaces)-2]
}
var buf = builder.NewWriter()
if _, err := buf.WriteString("INSERT INTO "); err != nil {
return "", nil, err
}
if err := statement.dialect.Quoter().QuoteTo(buf.Builder, tableName); err != nil {
return "", nil, err
}
if len(colPlaces) <= 0 {
if statement.dialect.URI().DBType == schemas.MYSQL {
if _, err := buf.WriteString(" VALUES ()"); err != nil {
return "", nil, err
}
} else {
if err := statement.writeInsertOutput(buf.Builder, table); err != nil {
return "", nil, err
}
if _, err := buf.WriteString(" DEFAULT VALUES"); err != nil {
return "", nil, err
}
}
} else {
if _, err := buf.WriteString(" ("); err != nil {
return "", nil, err
}
if err := statement.dialect.Quoter().JoinWrite(buf.Builder, append(colNames, exprs.ColNames...), ","); err != nil {
return "", nil, err
}
if statement.Conds().IsValid() {
if _, err := buf.WriteString(")"); err != nil {
return "", nil, err
}
if err := statement.writeInsertOutput(buf.Builder, table); err != nil {
return "", nil, err
}
if _, err := buf.WriteString(" SELECT "); err != nil {
return "", nil, err
}
if err := statement.WriteArgs(buf, args); err != nil {
return "", nil, err
}
if len(exprs.Args) > 0 {
if _, err := buf.WriteString(","); err != nil {
return "", nil, err
}
}
if err := exprs.WriteArgs(buf); err != nil {
return "", nil, err
}
if _, err := buf.WriteString(" FROM "); err != nil {
return "", nil, err
}
if err := statement.dialect.Quoter().QuoteTo(buf.Builder, tableName); err != nil {
return "", nil, err
}
if _, err := buf.WriteString(" WHERE "); err != nil {
return "", nil, err
}
if err := statement.Conds().WriteTo(buf); err != nil {
return "", nil, err
}
} else {
buf.Append(args...)
if _, err := buf.WriteString(")"); err != nil {
return "", nil, err
}
if err := statement.writeInsertOutput(buf.Builder, table); err != nil {
return "", nil, err
}
if _, err := buf.WriteString(" VALUES ("); err != nil {
return "", nil, err
}
if _, err := buf.WriteString(colPlaces); err != nil {
return "", nil, err
}
if err := exprs.WriteArgs(buf); err != nil {
return "", nil, err
}
if _, err := buf.WriteString(")"); err != nil {
return "", nil, err
}
}
}
if len(table.AutoIncrement) > 0 && statement.dialect.URI().DBType == schemas.POSTGRES {
if _, err := buf.WriteString(" RETURNING "); err != nil {
return "", nil, err
}
if err := statement.dialect.Quoter().QuoteTo(buf.Builder, table.AutoIncrement); err != nil {
return "", nil, err
}
}
return buf.String(), buf.Args(), nil
}

View File

@ -22,6 +22,16 @@ import (
"xorm.io/xorm/schemas"
)
// ErrFieldIsNotExist columns does not exist
type ErrFieldIsNotExist struct {
FieldName string
TableName string
}
func (e ErrFieldIsNotExist) Error() string {
return fmt.Sprintf("field %s is not valid on table %s", e.FieldName, e.TableName)
}
// ErrFieldIsNotValid is not valid
type ErrFieldIsNotValid struct {
FieldName string

View File

@ -326,8 +326,6 @@ func (session *Session) innerInsert(bean interface{}) (int64, error) {
return 0, ErrTableNotFound
}
table := session.statement.RefTable
// handle BeforeInsertProcessor
for _, closure := range session.beforeClosures {
closure(bean)
@ -338,100 +336,19 @@ func (session *Session) innerInsert(bean interface{}) (int64, error) {
processor.BeforeInsert()
}
var tableName = session.statement.TableName()
table := session.statement.RefTable
colNames, args, err := session.genInsertColumns(bean)
if err != nil {
return 0, err
}
exprs := session.statement.ExprColumns
colPlaces := strings.Repeat("?, ", len(colNames))
if exprs.Len() <= 0 && len(colPlaces) > 0 {
colPlaces = colPlaces[0 : len(colPlaces)-2]
}
var tableName = session.statement.TableName()
var output string
if session.engine.dialect.URI().DBType == schemas.MSSQL && len(table.AutoIncrement) > 0 {
output = fmt.Sprintf(" OUTPUT Inserted.%s", table.AutoIncrement)
}
var buf = builder.NewWriter()
if _, err := buf.WriteString(fmt.Sprintf("INSERT INTO %s", session.engine.Quote(tableName))); err != nil {
sqlStr, args, err := session.statement.GenInsertSQL(colNames, args)
if err != nil {
return 0, err
}
if len(colPlaces) <= 0 {
if session.engine.dialect.URI().DBType == schemas.MYSQL {
if _, err := buf.WriteString(" VALUES ()"); err != nil {
return 0, err
}
} else {
if _, err := buf.WriteString(fmt.Sprintf("%s DEFAULT VALUES", output)); err != nil {
return 0, err
}
}
} else {
if _, err := buf.WriteString(" ("); err != nil {
return 0, err
}
if err := session.engine.dialect.Quoter().JoinWrite(buf.Builder, append(colNames, exprs.ColNames...), ","); err != nil {
return 0, err
}
if session.statement.Conds().IsValid() {
if _, err := buf.WriteString(fmt.Sprintf(")%s SELECT ", output)); err != nil {
return 0, err
}
if err := session.statement.WriteArgs(buf, args); err != nil {
return 0, err
}
if len(exprs.Args) > 0 {
if _, err := buf.WriteString(","); err != nil {
return 0, err
}
}
if err := exprs.WriteArgs(buf); err != nil {
return 0, err
}
if _, err := buf.WriteString(fmt.Sprintf(" FROM %v WHERE ", session.engine.Quote(tableName))); err != nil {
return 0, err
}
if err := session.statement.Conds().WriteTo(buf); err != nil {
return 0, err
}
} else {
buf.Append(args...)
if _, err := buf.WriteString(fmt.Sprintf(")%s VALUES (%v",
output,
colPlaces)); err != nil {
return 0, err
}
if err := exprs.WriteArgs(buf); err != nil {
return 0, err
}
if _, err := buf.WriteString(")"); err != nil {
return 0, err
}
}
}
if len(table.AutoIncrement) > 0 && session.engine.dialect.URI().DBType == schemas.POSTGRES {
if _, err := buf.WriteString(" RETURNING " + session.engine.Quote(table.AutoIncrement)); err != nil {
return 0, err
}
}
sqlStr := buf.String()
args = buf.Args()
handleAfterInsertProcessorFunc := func(bean interface{}) {
if session.isAutoCommit {
for _, closure := range session.afterClosures {
@ -545,48 +462,48 @@ func (session *Session) innerInsert(bean interface{}) (int64, error) {
aiValue.Set(int64ToIntValue(id, aiValue.Type()))
return 1, nil
} else {
res, err := session.exec(sqlStr, args...)
if err != nil {
return 0, err
}
}
defer handleAfterInsertProcessorFunc(bean)
res, err := session.exec(sqlStr, args...)
if err != nil {
return 0, err
}
session.cacheInsert(tableName)
defer handleAfterInsertProcessorFunc(bean)
if table.Version != "" && session.statement.CheckVersion {
verValue, err := table.VersionColumn().ValueOf(bean)
if err != nil {
session.engine.logger.Errorf("%v", err)
} else if verValue.IsValid() && verValue.CanSet() {
session.incrVersionFieldValue(verValue)
}
}
session.cacheInsert(tableName)
if table.AutoIncrement == "" {
return res.RowsAffected()
}
var id int64
id, err = res.LastInsertId()
if err != nil || id <= 0 {
return res.RowsAffected()
}
aiValue, err := table.AutoIncrColumn().ValueOf(bean)
if table.Version != "" && session.statement.CheckVersion {
verValue, err := table.VersionColumn().ValueOf(bean)
if err != nil {
session.engine.logger.Errorf("%v", err)
} else if verValue.IsValid() && verValue.CanSet() {
session.incrVersionFieldValue(verValue)
}
}
if aiValue == nil || !aiValue.IsValid() || !aiValue.CanSet() {
return res.RowsAffected()
}
aiValue.Set(int64ToIntValue(id, aiValue.Type()))
if table.AutoIncrement == "" {
return res.RowsAffected()
}
var id int64
id, err = res.LastInsertId()
if err != nil || id <= 0 {
return res.RowsAffected()
}
aiValue, err := table.AutoIncrColumn().ValueOf(bean)
if err != nil {
session.engine.logger.Errorf("%v", err)
}
if aiValue == nil || !aiValue.IsValid() || !aiValue.CanSet() {
return res.RowsAffected()
}
aiValue.Set(int64ToIntValue(id, aiValue.Type()))
return res.RowsAffected()
}
// InsertOne insert only one struct into database as a record.