reform icon indicating copy to clipboard operation
reform copied to clipboard

Feature Request: Provide InsertIgnore and InsertIgnoreMany

Open evanmcclure opened this issue 6 years ago • 4 comments

Hi. We love the library. It's super simple and very powerful.

We'd love to see functionality for ignoring duplicate keys when inserting. Can you provide alternatives for Insert and InsertMany that provide INSERT IGNORE functionality?

You can find more information here:

  • MariaDB/MySQL: INSERT IGNORE - https://mariadb.com/kb/en/library/insert-ignore/
  • PostgreSQL: ON CONFLICT DO NOTHING - https://www.postgresql.org/docs/9.5/static/sql-insert.html#SQL-ON-CONFLICT

Thank you,

Evan Sunnyvale, CA

evanmcclure avatar Mar 27 '18 20:03 evanmcclure

Can you tell me more about your use case?

Will something like this work for you?

package reform

// CheckDuplicate returns nil if err is nil or duplicate key error.
// It returns err unmodified otherwise.
func CheckDuplicate(err error) error {
  …
}
if err := q.Insert(str); reform.CheckDuplicate(err) != nil {
  …
}

AlekSi avatar Mar 29 '18 07:03 AlekSi

Nice! Thank you, that will work nicely for INSERT (q.Insert()).

How about for multi-value inserts? (q.InsertMulti()). When I tried this in MariaDB, the entire INSERT is rejected if one of the value lists fails due to a conflict.

evanmcclure avatar Mar 29 '18 17:03 evanmcclure

What about something similar to what we currently have for querier.Select methods:

tail := "on conflict do nothing"
querier.Insert(record, tail)

Though that would work for postgres, but not with @evanmcclure's issue with the INSERT IGNORE when using MariaDB, so maybe a way to specify a prefix and a suffix?

I found two ways to deal with this with the current implementation with postgres: check the error class, or construct the query and directly call querier.Exec.

For reference, that's how you can do something similar to the ON CONFLICT DO NOTHING by checking the postgres error (simplified and incomplete version of the CheckDuplicate @AlekSi suggested):

	err = querier.Insert(record)
	pgErr, ok := err.(*pq.Error)
	if !ok || pgErr.Code.Class() != "23" { // 23 stands for "Class 23 — Integrity Constraint Violation" in postgres, see https://www.postgresql.org/docs/9.3/errcodes-appendix.html
		return err
	}

dgellow avatar Jul 23 '19 15:07 dgellow

Hmm, my solution isn't really one, because the conflict isn't handled on the SQL side any existing transaction is invalidated.

Edit: that's also valid for @AlekSi's suggestion using the CheckDuplicate.

dgellow avatar Jul 30 '19 15:07 dgellow