From f781fe26d039617bf39c64ee0a7f6a43db7cadaf Mon Sep 17 00:00:00 2001 From: gaoyang06 Date: Mon, 16 Nov 2020 15:16:34 +0800 Subject: [PATCH 1/4] break insert select into two queries to avoid deadlock --- join_table_handler.go | 12 ++++++------ 1 file changed, 6 insertions(+), 6 deletions(-) diff --git a/join_table_handler.go b/join_table_handler.go index a036d46d25..78a925a3f8 100644 --- a/join_table_handler.go +++ b/join_table_handler.go @@ -128,16 +128,16 @@ func (s JoinTableHandler) Add(handler JoinTableHandlerInterface, db *DB, source quotedTable := scope.Quote(handler.Table(db)) sql := fmt.Sprintf( - "INSERT INTO %v (%v) SELECT %v %v WHERE NOT EXISTS (SELECT * FROM %v WHERE %v)", + "INSERT INTO %v (%v) VALUES(%v)", quotedTable, strings.Join(assignColumns, ","), strings.Join(binVars, ","), - scope.Dialect().SelectFromDummyTable(), - quotedTable, - strings.Join(conditions, " AND "), ) - - return db.Exec(sql, values...).Error + checkSql := fmt.Sprintf("SELECT * FROM %v WHERE %v FOR UPDATE", quotedTable, strings.Join(conditions, " AND ")) + if db.Exec(checkSql); db.RowsAffected == 0 { + return db.Exec(sql, values...).Error + } + return nil } // Delete delete relationship in join table for sources From 54a80b6aa2740ff6c165548f48ccb0d507db4412 Mon Sep 17 00:00:00 2001 From: gaoyang06 Date: Mon, 16 Nov 2020 21:07:59 +0800 Subject: [PATCH 2/4] handle sqlite3 case --- join_table_handler.go | 11 ++++++----- 1 file changed, 6 insertions(+), 5 deletions(-) diff --git a/join_table_handler.go b/join_table_handler.go index 78a925a3f8..e0d389989f 100644 --- a/join_table_handler.go +++ b/join_table_handler.go @@ -122,10 +122,6 @@ func (s JoinTableHandler) Add(handler JoinTableHandlerInterface, db *DB, source values = append(values, value) } - for _, value := range values { - values = append(values, value) - } - quotedTable := scope.Quote(handler.Table(db)) sql := fmt.Sprintf( "INSERT INTO %v (%v) VALUES(%v)", @@ -134,7 +130,12 @@ func (s JoinTableHandler) Add(handler JoinTableHandlerInterface, db *DB, source strings.Join(binVars, ","), ) checkSql := fmt.Sprintf("SELECT * FROM %v WHERE %v FOR UPDATE", quotedTable, strings.Join(conditions, " AND ")) - if db.Exec(checkSql); db.RowsAffected == 0 { + if db.Dialect().GetName() == "sqlite3" { + // sqlite does not support SELECT FOR UPDATE. But it is single thread concurrency, safe here. + checkSql = fmt.Sprintf("SELECT * FROM %v WHERE %v", quotedTable, strings.Join(conditions, " AND ")) + } + + if db.Exec(checkSql, values...); db.Error == nil && db.RowsAffected == 0 { return db.Exec(sql, values...).Error } return nil From 21cdaeac6f3cbfb9ca6e1d9f5837699d56822f30 Mon Sep 17 00:00:00 2001 From: gaoyang06 Date: Tue, 17 Nov 2020 11:02:57 +0800 Subject: [PATCH 3/4] fix failed unit test cases --- join_table_handler.go | 14 ++++++++++---- 1 file changed, 10 insertions(+), 4 deletions(-) diff --git a/join_table_handler.go b/join_table_handler.go index e0d389989f..6e2b9e2a3a 100644 --- a/join_table_handler.go +++ b/join_table_handler.go @@ -129,13 +129,19 @@ func (s JoinTableHandler) Add(handler JoinTableHandlerInterface, db *DB, source strings.Join(assignColumns, ","), strings.Join(binVars, ","), ) - checkSql := fmt.Sprintf("SELECT * FROM %v WHERE %v FOR UPDATE", quotedTable, strings.Join(conditions, " AND ")) + checkSql := fmt.Sprintf("SELECT COUNT(1) FROM %v WHERE %v FOR UPDATE", quotedTable, strings.Join(conditions, " AND ")) if db.Dialect().GetName() == "sqlite3" { // sqlite does not support SELECT FOR UPDATE. But it is single thread concurrency, safe here. - checkSql = fmt.Sprintf("SELECT * FROM %v WHERE %v", quotedTable, strings.Join(conditions, " AND ")) + checkSql = fmt.Sprintf("SELECT COUNT(1) FROM %v WHERE %v", quotedTable, strings.Join(conditions, " AND ")) } - - if db.Exec(checkSql, values...); db.Error == nil && db.RowsAffected == 0 { + checkCount := 0 + rows, err := db.db.Query(checkSql, values...) + if err != nil { + return err + } + rows.Next() + rows.Scan(&checkCount) + if checkCount == 0 { return db.Exec(sql, values...).Error } return nil From f5eccf5a5fe28d61adbb3ff9d5ee10e5c75b23da Mon Sep 17 00:00:00 2001 From: gaoyang06 Date: Tue, 17 Nov 2020 11:10:43 +0800 Subject: [PATCH 4/4] close cursor after many to many handler check --- join_table_handler.go | 1 + 1 file changed, 1 insertion(+) diff --git a/join_table_handler.go b/join_table_handler.go index 6e2b9e2a3a..2e2df2838d 100644 --- a/join_table_handler.go +++ b/join_table_handler.go @@ -141,6 +141,7 @@ func (s JoinTableHandler) Add(handler JoinTableHandlerInterface, db *DB, source } rows.Next() rows.Scan(&checkCount) + rows.Close() if checkCount == 0 { return db.Exec(sql, values...).Error }