From c9dfd80959ef31311810835abeaaf5d07131089c Mon Sep 17 00:00:00 2001 From: Jinzhu Date: Mon, 15 Feb 2016 19:36:48 +0800 Subject: [PATCH] Support extra options for inserting, querying, deleting, updating SQL, close #721, #769 --- README.md | 17 +++++++++++++++++ callback_create.go | 17 ++++++++++++++--- callback_delete.go | 25 ++++++++++++++++++------- callback_query.go | 7 ++++++- callback_update.go | 11 ++++++++++- utils.go | 7 +++++++ 6 files changed, 72 insertions(+), 12 deletions(-) diff --git a/README.md b/README.md index 9535015e..5751964f 100644 --- a/README.md +++ b/README.md @@ -259,6 +259,11 @@ db.Create(&user) //// INSERT INTO "languages" ("name") VALUES ('EN'); //// INSERT INTO user_languages ("user_id","language_id") VALUES (111, 2); //// COMMIT; + + +// Add extra SQL option for inserting SQL +db.Set("gorm:insert_option", "ON CONFLICT").Create(&product) +// INSERT INTO products (name, code) VALUES ("name", "code") ON CONFLICT; ``` Refer [Associations](#associations) for more details @@ -281,6 +286,10 @@ db.Find(&users) // Get record with primary key db.First(&user, 10) //// SELECT * FROM users WHERE id = 10; + +// Add extra SQL option for selecting SQL +db.Set("gorm:query_option", "FOR UPDATE").First(&user, 10) +//// SELECT * FROM users WHERE id = 10 FOR UPDATE; ``` ### Query With Where (Plain SQL) @@ -460,6 +469,10 @@ db.Model(&user).Updates(map[string]interface{}{"name": "hello", "age": 18, "acti // Update multiple attributes if they are changed (update with struct only works with none zero values) db.Model(&user).Updates(User{Name: "hello", Age: 18}) //// UPDATE users SET name='hello', age=18, updated_at = '2013-11-17 21:34:10' WHERE id = 111; + +// Add extra SQL option for updating SQL +db.Model(&user).Set("gorm:update_option", "OPTION (OPTIMIZE FOR UNKNOWN)").Update("name, "hello") +//// UPDATE users SET name='hello', updated_at = '2013-11-17 21:34:10' WHERE id=111 OPTION (OPTIMIZE FOR UNKNOWN); ``` ### Update Without Callbacks @@ -513,6 +526,10 @@ DB.Model(&product).Where("quantity > 1").UpdateColumn("quantity", gorm.Expr("qua // Delete an existing record db.Delete(&email) //// DELETE from emails where id=10; + +// Add extra SQL option for deleting SQL +db.Set("gorm:delete_option", "OPTION (OPTIMIZE FOR UNKNOWN)").Delete(&email) +//// DELETE from emails where id=10 OPTION (OPTIMIZE FOR UNKNOWN); ``` ### Batch Delete diff --git a/callback_create.go b/callback_create.go index 0ba3feac..6316f9ee 100644 --- a/callback_create.go +++ b/callback_create.go @@ -75,8 +75,13 @@ func createCallback(scope *Scope) { returningColumn = "*" quotedTableName = scope.QuotedTableName() primaryField = scope.PrimaryField() + extraOption string ) + if str, ok := scope.Get("gorm:insert_option"); ok { + extraOption = fmt.Sprint(str) + } + if primaryField != nil { returningColumn = scope.Quote(primaryField.DBName) } @@ -84,14 +89,20 @@ func createCallback(scope *Scope) { lastInsertIdReturningSuffix := scope.Dialect().LastInsertIdReturningSuffix(quotedTableName, returningColumn) if len(columns) == 0 { - scope.Raw(fmt.Sprintf("INSERT INTO %v DEFAULT VALUES %v", quotedTableName, lastInsertIdReturningSuffix)) + scope.Raw(fmt.Sprintf( + "INSERT INTO %v DEFAULT VALUES%v%v", + quotedTableName, + addExtraSpaceIfExist(extraOption), + addExtraSpaceIfExist(lastInsertIdReturningSuffix), + )) } else { scope.Raw(fmt.Sprintf( - "INSERT INTO %v (%v) VALUES (%v) %v", + "INSERT INTO %v (%v) VALUES (%v)%v%v", scope.QuotedTableName(), strings.Join(columns, ","), strings.Join(placeholders, ","), - lastInsertIdReturningSuffix, + addExtraSpaceIfExist(extraOption), + addExtraSpaceIfExist(lastInsertIdReturningSuffix), )) } diff --git a/callback_delete.go b/callback_delete.go index b3a77926..9db0666c 100644 --- a/callback_delete.go +++ b/callback_delete.go @@ -21,15 +21,26 @@ func beforeDeleteCallback(scope *Scope) { // deleteCallback used to delete data from database or set deleted_at to current time (when using with soft delete) func deleteCallback(scope *Scope) { if !scope.HasError() { + var extraOption string + if str, ok := scope.Get("gorm:delete_option"); ok { + extraOption = fmt.Sprint(str) + } + if !scope.Search.Unscoped && scope.HasColumn("DeletedAt") { - scope.Raw( - fmt.Sprintf("UPDATE %v SET deleted_at=%v %v", - scope.QuotedTableName(), - scope.AddToVars(NowFunc()), - scope.CombinedConditionSql(), - )).Exec() + scope.Raw(fmt.Sprintf( + "UPDATE %v SET deleted_at=%v%v%v", + scope.QuotedTableName(), + scope.AddToVars(NowFunc()), + addExtraSpaceIfExist(scope.CombinedConditionSql()), + addExtraSpaceIfExist(extraOption), + )).Exec() } else { - scope.Raw(fmt.Sprintf("DELETE FROM %v %v", scope.QuotedTableName(), scope.CombinedConditionSql())).Exec() + scope.Raw(fmt.Sprintf( + "DELETE FROM %v%v%v", + scope.QuotedTableName(), + addExtraSpaceIfExist(scope.CombinedConditionSql()), + addExtraSpaceIfExist(extraOption), + )).Exec() } } } diff --git a/callback_query.go b/callback_query.go index 8067c855..0221c322 100644 --- a/callback_query.go +++ b/callback_query.go @@ -51,8 +51,13 @@ func queryCallback(scope *Scope) { scope.prepareQuerySql() if !scope.HasError() { + var extraOption string + if str, ok := scope.Get("gorm:query_option"); ok { + extraOption = fmt.Sprint(str) + } scope.db.RowsAffected = 0 - if rows, err := scope.SqlDB().Query(scope.Sql, scope.SqlVars...); scope.Err(err) == nil { + + if rows, err := scope.SqlDB().Query(scope.Sql+addExtraSpaceIfExist(extraOption), scope.SqlVars...); scope.Err(err) == nil { defer rows.Close() columns, _ := rows.Columns() diff --git a/callback_update.go b/callback_update.go index b3a6c7da..44f9a143 100644 --- a/callback_update.go +++ b/callback_update.go @@ -90,9 +90,18 @@ func updateCallback(scope *Scope) { } } + var extraOption string + if str, ok := scope.Get("gorm:update_option"); ok { + extraOption = fmt.Sprint(str) + } + if len(sqls) > 0 { scope.Raw(fmt.Sprintf( - "UPDATE %v SET %v %v", scope.QuotedTableName(), strings.Join(sqls, ", "), scope.CombinedConditionSql(), + "UPDATE %v SET %v%v%v", + scope.QuotedTableName(), + strings.Join(sqls, ", "), + addExtraSpaceIfExist(scope.CombinedConditionSql()), + addExtraSpaceIfExist(extraOption), )).Exec() } } diff --git a/utils.go b/utils.go index 94e345cc..bfdaf9f7 100644 --- a/utils.go +++ b/utils.go @@ -276,3 +276,10 @@ func getValueFromFields(value reflect.Value, fieldNames []string) (results []int } return } + +func addExtraSpaceIfExist(str string) string { + if str != "" { + return " " + str + } + return "" +}