Reputation: 2209
I'm using gorm with postgres in my Go app.
I want to create a new user in the database, but there is a good chance that that user will already exist. If so, I want to not do anything with the database, but I want know about it so I can tell the user.
The good news is, that's already what gorm.Create(..)
does. Trying to create a record with a duplicate unique key will return an error. There are two problems:
Create()
which seems bug prone.Create()
with an object that already exists logs an error message to stdout. I don't really consider this an "error" since I was expecting it to happen and I don't want to flood my logs with a bunch of these warnings.I know I could use a transaction to first check for a user with the given id and then create them if one does not already exist, but it seems like there should be a simpler solution to such a basic thing. How are you supposed to do this?
I'm currently doing this:
func (self databaseWrapper) CreateUser(user *User) error {
db := self.db
db.NewRecord(*user)
err := db.Create(user).Error
if err != nil {
if db.Where(user.ID).Take(&User{}).Error == nil {
return fmt.Errorf("A user already exists with id %v", user.ID)
}
if db.Where(User{Email: user.Email}).Take(&User{}).Error == nil {
return fmt.Errorf("A user already exists with the given email address: %v", user.Email)
}
return fmt.Errorf("Error creating user")
}
return nil
}
Which is a little inefficient and gives the ugly output:
go test
(/home/quinn/workspace/aev/sensor/backend/server/database.go:125)
[2019-09-01 14:45:40] pq: duplicate key value violates unique constraint "users_pkey"
(/home/quinn/workspace/aev/sensor/backend/server/database.go:125)
[2019-09-01 14:45:40] pq: duplicate key value violates unique constraint "uix_users_email"
PASS
ok 3.215s
even when everything worked as expected.
Upvotes: 11
Views: 11224
Reputation: 17412
In Gorm v1.21, I believe you could...
import (
gormLogger "gorm.io/gorm/logger"
)
func main() {
db, err := gorm.Open(
sqlite.Open("test.db"),
&gorm.Config{
Logger: gormLogger.New(
log.New(os.Stdout, "\r\n", log.LstdFlags),
gormLogger.Config {
LogLevel: gormLogger.Silent,
// IgnoreRecordNotFoundError: true,
},
),
},
)
}
result := db.Where("email = ?", "[email protected]").Take(&user)
if !errors.Is(result.Error, gorm.ErrRecordNotFound) {
fmt.Println("record already exists")
// Or use a logger
// logger.Info("record already exists")
}
Upvotes: 2
Reputation: 1359
To add to the previous answer, currently you can also use something like this to check for error codes (in the example: checking for duplicate keys).
import "github.com/jackc/pgx"
...
func isDuplicateKeyError(err error) bool {
pgErr, ok := err.(pgx.PgError)
if ok {
// unique_violation = 23505
return pgErr.Code == "23505"
}
return false
}
Upvotes: 5
Reputation: 2495
lib/pq
is the standard postgres driver. If there is a query error, it will return a pq.Error
object (even if you're using GORM). The pq.Error
type has a Code
field you can inspect to see the cause of the error.
if err, ok := err.(*pq.Error); ok && err.Code.Name() == "unique_violation" {
// handle error
}
Upvotes: 4