Access Unique Constraint error in lib/pq - postgresql

I would like to handle a postgresql unique constraint error by accessing the error code. I have gone through these articles here and here and also gone through the documentation here, but still don't quite understand how to implement this and access the errorcode returned by the db. It seems only the Message field method was implemented:
func (err Error) Error() string {
return "pq: " + err.Message
}
If I want to access the SQLSTATE code, Do I implement something like:
func (err *Error) Error() string {
return err.Code
}
and assume the Error struct as defined here will be available.
I have tried something in my handler like this:
sqlInsert := INSERT INTO usrtable (usrCode, teamName, email, phone) VALUES ($1,$2,$3,$4)
_, err := db.Exec(sqlInsert, Data.UsrCode, Data.Teamname, Data.Email, Data.Phone)
if err != nil {
switch err {
case errorCodeNames["23505"]:
// Return web page identifying field and advising user what to do.
return
This returns undefined which makes sense since errorCodeNames is not exported but I am stumped regarding how to achieve this.

You could do something like this if you are using Postgres:
if pgErr, ok := err.(*pq.Error); ok {
if pgErr.Code == "23505" {
//handle duplicate insert
}
}

Related

Validation using golang, gin and gorm with postgres like email already exist in database

I have something like this and I would like to check if email already exist in DB:
func RegisterUser(c *gin) {
var user models.User
if err := c.ShouldBindJSON(&user); err != nil {
c.JSON(http.StatusBadRequest, gin.H{
"messAge": err.Error(),
"data": "",
})
return
}
// **I TRIED SOEMTHING LIKE THIS**
err := database.DB.Find(&user.Email).Error
if err != nil {
c.JSON(401, gin.H{"MESSAGE": "Email ALREADY exist",
return
}
// **but is not working, because ANY mail it give me error**
if !strings.Contains(user.Email, "#") {
c.JSON(400, gin.H{"MESSAGE": utils.ErrEmailWrong})
return
}
if len(user.Password) < 4 {
c.JSON(400, gin.H{"MESSAGE": utils.ErrPasswordLength})
return
}
database.DB.Create(&user)
c.JSON(200, gin.H{
"MESSAGE": "CREATED",
})
}
With this code, every time is telling me that : Email already exist, only works for the first time.
plase read the document:
https://gorm.io/docs/query.html
var userFind models.User
database.DB.Where("email = ?", user.Email).First(&userFind)
Since, your struct object is not a slice. You should use ErrRecordNotFound.
Note : ErrRecordNotFound only works with First, Last, Take which is expected to return some result. And RecordNotFound is removed in V2.
if err != nil {
if errors.Is(err, gorm.ErrRecordNotFound){
c.JSON(401, gin.H{"MESSAGE": "Email Not Found",
return
}
c.JSON(401, gin.H{"MESSAGE": "Your Message",
return
}
OR
If you want to avoid the ErrRecordNotFound error, you could use Find like db.Limit(1).Find(&user), the Find method accepts both struct and slice data. And check like this :
result.RowsAffected // returns count of records found
For better understanding refer the link here : https://gorm.io/docs/v2_release_note.html#ErrRecordNotFound and https://gorm.io/docs/query.html
And, If you want to add record in DB though email exist then you should remove unique constraint and also check the error while creating the record. If record successfully created then return success response else return the appropriate error message.
you should validate the input after binding and before db queries
alter your email column to be unique
try to insert the validated data to db
if success => 200 (there was no similar email)
if err => check err code
for example:
func IsUniqueContraintViolation(err error) bool {
if pgError, ok := err.(*pgconn.PgError); ok && errors.Is(err, pgError) {
if pgError.Code == "23505" {
return true
}
}
return false
}
For more Information, you should look GoDoc pg lib and Possible Error Codes
and then, then you can return a suitable error code
btw. hopefully you don't save clear passwords to db :D
ITS FINALLY WORKING ! Thanks for everyone thats answered !

Return postgres errors in api response

I have two simple api methods in my code. Method with endpoind /api/user/create creates user. Field username is unique. When i trying to create user with same username that already exists in database, i have an error in console:
(/home/andrej/go/src/go_contacts/models/users.go:19)
[2020-12-23 22:03:10] pq: duplicate key value violates unique constraint "users_username_key"
I want to show this error in response to user, or somehow identify type of error in my code, to show different error messages for user. I know only that if i have error user returns me id=0. But it doesnt seems like a good message for user.
main.go
package main
import (
"fmt"
"go_contacts/controllers"
"net/http"
"os"
"github.com/gorilla/mux"
"github.com/joho/godotenv"
)
func main() {
godotenv.Load(".env")
router := mux.NewRouter()
router.HandleFunc("/", controllers.ReturnHello).Methods("GET")
router.HandleFunc("/api/user/create", controllers.CreateUser).Methods("POST")
port := os.Getenv("PORT")
if port == "" {
port = "8000"
}
err := http.ListenAndServe(":"+port, router)
if err != nil {
fmt.Print(err)
}
}
models.go with user struct:
package models
import (
u "go_contacts/utils"
"github.com/jinzhu/gorm"
)
// User base model
type User struct {
gorm.Model
Username string `json:"username" gorm:"unique"`
Password string `json:"password"`
Email string `json:"email"`
}
// Create new user
func (user *User) Create() map[string]interface{} {
GetDB().Create(user)
if user.ID <= 0 {
return u.Message(false, "Failed to create user, connection error.")
}
response := u.Message(true, "Account has been created")
response["user"] = user
return response
}
As for pq v1.5.2 and gorm v1.9.12
First, you need to identify whether create method call returns error or not. Like so
err := GetDB().Create(user).Error
if err != nil {
// code to handle error
}
Then the pq package has special type which maps to postgres server error. It contains fields which can help you to identify the error severity, it's code, table which related to error etc.
Full list of psql fields identifiers can be found here in
Error and Notice Message Fields
https://www.postgresql.org/docs/current/protocol-error-fields.html
To resolve your issue as an option we can use field
Code
Which is a string type representation of error code. But firstly, we need to cast an error and check type cast was successful. Like so
err := GetDB().Create(user).Error
if err != nil {
pqErr, ok := err.(pq.Error)
if !ok {
log.Fatal(err)
}
// code to handle specific error code
}
List of error codes can be found both in the official postgresql docs https://www.postgresql.org/docs/9.3/errcodes-appendix.html
And actual go pq specific mappings to the error in the github.com/lib/pq/error.go as for pq library
And finally we can handle error as duplicate by code
err := GetDB().Create(user).Error
if err != nil {
pqErr, ok := err.(pq.Error)
if !ok {
log.Fatal(err)
}
// note that type casting here is redundant and used for showing specific
// string type from pq library
if pqErr.Code == pq.ErrorCode("23505") { // 23505 is unique_violation error code for psql
// now you can create error and write it to a message to return it for
// a client
}
}
As per the comments gorm provides access to database errors from the Create record function as follows:
result := GetDB().Create(user)
if result.Error != nil {
// Do something with the error
}
Please note that checking the error string is likely to make your application database specific (but this may not be an issue for you). The answers to this question may provide further assistance.

Incorrect ModifiedCount and MatchedCount using mongodb/mongo-go-driver and Azure CosmosDB

I am trying to write a simple CRUD test application using mongodb/mongo-go-driver (v1.2.1) which connects to an Azure CosmosDB instance (v3.6). Consider the following code excerpts.
Stripped down Update function from Client struct which is omitted for brevity
func (c *Client) Update(ctx context.Context, filter, update bson.M) error {
res, err := c.collection.UpdateOne(ctx, filter, update, options.Update())
if err != nil {
return Error{ErrFunctional, "failed to update document", err}
}
if res.MatchedCount != 1 {
return Error{
Code: ErrNotFound,
msg: "document not found",
}
}
if res.ModifiedCount != 1 {
return Error{
Code: ErrNotUpdated,
msg: "document not updated",
}
}
return nil
}
Runner code looks like this
type doc struct {
count int
}
id, err := dbClient.Insert(context.TODO(), doc{1})
if err != nil {
panic(fmt.Errorf("insert failed: %v", err))
}
err = dbClient.Update(context.TODO(), bson.M{"_id": id}, bson.M{"$set": bson.M{"count": 2}})
if err != nil {
panic(fmt.Errorf("update failed: %v", err))
}
err = dbClient.Delete(context.TODO(), bson.M{"_id": id})
if err != nil {
panic(fmt.Errorf("delete failed: %v", err))
}
As you can see in the code, I am trying to accomplish the following steps:
Insert a record {"count": 1} (This works correctly and document is inserted)
Update the insert record to {"count": 2} (Fails due to no document found error)
Delete the record (Code never reaches here)
The program fails at the 2nd step. I inspected the result returned by the driver and both MatchedCount and ModifiedCount are always 0. However the database was updated with correct data. Weird, right? Now interestingly, if I execute the same steps using MongoDB shell (CLI, Installed using brew) then the steps finish without any problems.
I have tried all variations of the filters and update statements to make it work but to no avail. I have a feeling that it has to do with the Golang driver. Is there something I am missing or doing wrong? Kindly feel free to ask for more information and I will gladly edit the question to provide it.
Turns out it was just a silly mistake in the connection string. For some reason I was using this format.
mongodb://<username>:<password>#<app>.documents.azure.com:10255/?ssl=true&replicaSet=globaldb&maxIdleTimeMS=120000&retrywrites=false"
Changed to this format and everything works now.
mongodb://<username>:<password>#<app>.mongo.cosmos.azure:10255/?ssl=true&replicaSet=globaldb&maxIdleTimeMS=120000&retrywrites=false"
SMH.

Example in database/sql using sql.ErrNoRows crashes and burns

I have a small Go program which uses a a postgresql db. In it there is a query which can return no rows, and the code I'm using to deal with this isn't working correctly.
// Get the karma value for nick from the database.
func getKarma(nick string, db *sql.DB) string {
var karma int
err := db.QueryRow("SELECT SUM(delta) FROM karma WHERE nick = $1", nick).Scan(&karma)
var karmaStr string
switch {
case err == sql.ErrNoRows:
karmaStr = fmt.Sprintf("%s has no karma.", nick)
case err != nil:
log.Fatal(err)
default:
karmaStr = fmt.Sprintf("Karma for %s is %d.", nick, karma)
}
return karmaStr
}
This logic is taken directly from the Go documentation. When there are no rows corresponding to nick, the following error occurs:
2016/07/24 19:37:07 sql: Scan error on column index 0: converting driver.Value type <nil> ("<nil>") to a int: invalid syntax
I must be doing something stupid - clues appreciated.
I believe your issue is that you're getting a NULL value back from the database, which go translates into nil. However, you're scanning into an integer, which has no concept of nil. One thing you can do is scan into a type that implements the sql.Scanner interface (and can handle NULL values), e.g., sql.NullInt64.
In the example code in the documentation, I'd assume they have a NOT NULL constraint on the username column. I think the reason for this is because they didn't want to lead people to believe that you have to use NULL-able types across the board.
I reworked the code to get the results I wanted.
// Get the karma value for nick from the database.
func getKarma(nick string, db *sql.DB) string {
var karma int
rows, err := db.Query("SELECT SUM(delta) FROM karma WHERE nick = $1", nick)
if err != nil {
log.Fatal(err)
}
defer rows.Close()
karmaStr := fmt.Sprintf("%s has no karma.", nick)
if rows.Next() {
rows.Scan(&karma)
karmaStr = fmt.Sprintf("Karma for %s is %d.", nick, karma)
}
return karmaStr
}
Tempted to submit a documentation patch of some sort to the database/sql package.

Dynamic casting in Golang

So... I'm creating a RESTful API for my idea using Gin framework and I've came into the following problem -
Let's say that I've got the following endpoints:
/a/:id/*action
/b/:id/*action
/c/:id/*action
So, obviously, when I'm not giving any action then I want to return the data for the given ID. Meaning, I'm doing nothing but querying some data and returning it, this means that the functionality is basically the same and only the returned data is different.
Here's an example code of mine -
func GetBusiness(c *gin.Context) {
businessID, err := strconv.Atoi(c.Param("id"))
if businessID == 0 || err != nil {
c.JSON(http.StatusBadRequest, gin.H{"success": false, "errorMessage": "Missing ID"})
}
business := &Business{}
business, err = business.Get(businessID)
if err != nil {
c.JSON(http.StatusBadRequest, gin.H{"success": false, "errorMessage": "Business not found"})
}
c.JSON(http.StatusOK, business)
}
So, obviously, business can become user or anything else. So, after this long exposition, my question to you goers, is, how can I prevent code duplication in this kind of situation? I've tried using an interface but I'm still struggling with the OO nature of Go, so I would really appriciate any help.
Thanks in advance!
There are a few things you can do to reduce code duplication, but unfortunately, you will always be writing some boilerplate in go, because of it's explicit error handling and lack of OOP-ness. (which is not necessarily a bad thing!).
So my only suggestions at the moment is to put common functionality in middleware handlers and restructure your code a litte, for example:
parseIdMiddleware := func(c *gin.Context) {
id, err := strconv.Atoi(c.Param("id"))
if businessID == 0 || err != nil {
c.AbortWithError(http.StatusBadRequest, errors.New("Missing ID"))
return
}
c.Set("id", id)
}
...
gin.Use(gin.ErrorLogger(), parseIdMiddleware)
and rewrite your handlers to
func GetBusiness(c *gin.Context) {
id := c.MustGet("id").(int)
business, err := store.GetBusiness(id)
if err != nil {
c.AbortWithError(http.StatusBadRequest, err)
return // don't forget this!
}
c.JSON(http.StatusOK, business)
}
And as always, read other people's code! I recommend https://github.com/drone/drone. That should give you a pretty good overview of how to structure your code.