2
votes

I'm trying to code some simple CRUD focused in employees management, but in this point I'm a little confused so how I should to use struct attributes json:... and gorm: for declaring specific data types and constraints to the new database table and specific values sended in json format from Postman. Are you working with Go-Gorm?

//db file
package tiposDocumentos

import (
    "bitbucket.org/username/repositoryname/db"
    "bitbucket.org/username/repositoryname/models"
)

// Inserta un nuevo tipo de documento en la base de datos.
func InsertTipoDocumento(tp models.TiposDocumentos) (bool, error) {
    db.PsqlDb.AutoMigrate(&tp)
    if db.PsqlDb.NewRecord(tp) {
        err := db.PsqlDb.Create(&tp)
        if err != nil {
            return false, nil
        }
    } else {
        return false, errors.New("clave primaria se encuentra en uso")
    }

    return true, nil
}
package models
    
type TiposDocumentos struct {
        TdoCodigo       string  `json:"tdo_codigo" gorm:"column:tdo_codigo;type:varchar(3);PRIMARY_KEY;AUTO_INCREMENT:false"`
        TdoDescripcion  string  `json:"tdo_descripcion" gorm:"column:tdo_descripcion;type:varchar(50)"`
        TdoCodigoAfip   string  `json:"tdo_codigo_afip" gorm:"column:tdo_codigo_afip;type:varchar(10)"`
}
package routers

import (
    "bitbucket.org/username/repositoryname/db/tiposDocumentos"
    "bitbucket.org/username/repositoryname/models"
    "encoding/json"
    "fmt"
    "net/http"
)

var (
    tp models.TiposDocumentos
)

// Valida registro recibido y lo inserta en la base de datos.
func PostTiposDocuemntos(w http.ResponseWriter, r *http.Request) {
    err := json.NewDecoder(r.Body).Decode(&tp)
    if err != nil {
        http.Error(w, err.Error(), http.StatusBadRequest)
        return
    }

    estado, err := tiposDocumentos.InsertTipoDocumento(tp)
    if estado == false {
        http.Error(w, err.Error(), http.StatusBadRequest)
        return
    }
    w.WriteHeader(http.StatusCreated)
}

In this point, after send the request from Postman the program create successfully the table and its specifications and retrieve code 201 but the record was not inserted in the table. enter image description here View from pgAdmin4 enter image description here

Thanks ☻

Error after insert sucesfuly a new record in the database:

2020/07/13 12:43:28 http: panic serving 127.0.0.1:64546: runtime error: invalid memory address or nil pointer dereference
goroutine 36 [running]:
net/http.(*conn).serve.func1(0xc000230d20)
        C:/Go/src/net/http/server.go:1772 +0x140
panic(0x8085e0, 0xbb5e20)
        C:/Go/src/runtime/panic.go:975 +0x3f1
bitbucket.org/emanuelvald/gestion_personal/routers.PostTiposDocuemntos(0x912c20, 0xc00005e000, 0xc000058200)
        C:/Projects/Go/src/bitbucket.org/emanuelvald/gestion_personal/routers/tiposDocumentos.go:44 +0x151
net/http.HandlerFunc.ServeHTTP(0x893498, 0x912c20, 0xc00005e000, 0xc000058200)
        C:/Go/src/net/http/server.go:2012 +0x4b
github.com/gorilla/mux.(*Router).ServeHTTP(0xc00022a0c0, 0x912c20, 0xc00005e000, 0xc000058000)
        C:/Projects/Go/src/github.com/gorilla/mux/mux.go:210 +0xe9
net/http.serverHandler.ServeHTTP(0xc000270000, 0x912c20, 0xc00005e000, 0xc000058000)
        C:/Go/src/net/http/server.go:2807 +0xaa
net/http.(*conn).serve(0xc000230d20, 0x9137a0, 0xc000038080)
        C:/Go/src/net/http/server.go:1895 +0x873
created by net/http.(*Server).Serve
        C:/Go/src/net/http/server.go:2933 +0x363

1
When this if db.PsqlDb.NewRecord(tp) { is not satisfied you are returning true, nil which you then interpret as "everything went well" even though it obviously did not. Fix the logic first. - mkopriva
I fixed this problem and the the NewRecord function return false as if primary key was used but the table is empty!!! - Young Al Capone
Add the full stack trace of the panic to the question and I'll point out the exact file and line that's causing the crash. But if I had to guess, gorm Create returns the *DB instance and not an error, so the err variable is always a non-nil *DB, and therefore if err != nil { is always satisfied... then in that branch you return false, nil and subsequently you have the caller invoke err.Error() on that nil error value and boom! - mkopriva
The proper way to check gorm errors is to inspect the Error field of the *DB instance. i.e. err := db.Create(&tp).Error and then if err is not nil, you need to return it to the caller return false, err. - mkopriva
The stack trace shows that the origin of the panic is coming from the routers.PostTiposDocuemntos function in file ...routers/tiposDocumentos.go on line 44. - mkopriva

1 Answers

2
votes

Consider what happens when err is non-nil here:

 if err != nil {
     return false, nil
 }

and then you do this:

estado, err := tiposDocumentos.InsertTipoDocumento(tp)
if estado == false {
    http.Error(w, err.Error(), http.StatusBadRequest)
    return
}

So, estado is false, and then you try to access err.Error(), but err is nil, so you get a

invalid memory address or nil pointer dereference

You probably just need to do:

 if err != nil {
     return false, err
 }

and then do:

if err != nil {
    http.Error(w, err.Error(), http.StatusBadRequest)
    return
}