Difference in string and *string in Gorm model declaration - go

The docs of gorm https://gorm.io/docs/models.html present an example below.
The field Name and Email are described with string and *string.
What is the main difference here?
Also how to provide the datatype for the images field storing a list of images link?
Should it be []string or []*string?
type User struct {
ID uint
Name string
Email *string
Images []string
Age uint8
Birthday *time.Time
MemberNumber sql.NullString
ActivatedAt sql.NullTime
CreatedAt time.Time
UpdatedAt time.Time
}

Go has default values for every primitive data types.
int -> 0, string -> "", bool -> false likewise. So if you need to add null value, or load null value to a variable, it should be a pointer. Otherwise it is defaulted.
Default value of a pointer is nil in Go.
And complex data types such as slices, maps keep references. So their default value is nil. So, Images []string here images can be nil.
Below code with pointer types User1 and without pointer types User2 show the difference in default values.
package main
import (
"fmt"
"time"
)
type User1 struct {
Email *string
Images []string
Birthday *time.Time
}
type User2 struct {
Email string
Images []string
Birthday time.Time
}
func main() {
user1 := User1{}
user2 := User2{}
fmt.Printf("user1 := %+v \n", user1)
//Output : user1 := {Email:<nil> Images:[] Birthday:<nil>}
fmt.Printf("user2 := %+v \n", user2)
//Output : user2 := {Email: Images:[] Birthday:0001-01-01 00:00:00 +0000 UTC}
}

The main difference is that if you use pointers, you can put a null value into the DB, else you must put a string.
Esentially if the database field is nullable, you should use pointers.

Related

db.First() not using primary key name

I am writing some sample code to understand gorm but appear to be having an issue with setting up the primary_key value. As a result, the resulting SQL query is broken.
Please see the sample code:
package main
import (
"os"
"fmt"
"time"
"gorm.io/driver/postgres"
"gorm.io/gorm"
)
type Post struct {
id int `json:"id" gorm:"primary_key:id"`
url string `gorm:"url"`
content string `gorm:"content"`
created_at time.Time `gorm:"created_at"`
normalized string `gorm:"normalized"`
account_id int `gorm:"account_id"`
posthash []byte `gorm:"posthash"`
received_at time.Time `gorm:"received_at"`
}
func main() {
dsn := "user=postgres dbname=localtest"
db, err := gorm.Open(postgres.Open(dsn), &gorm.Config{})
if err != nil {
fmt.Println("An error occurred", err)
os.Exit(1)
}
db.AutoMigrate(&Post{})
var post Post
db.First(&post, 1)
fmt.Println(post)
}
When I run this code, I receive the following error:
$ go run gorm_test.go
2020/12/01 00:34:06 /home/farhan/gorm_test.go:32 ERROR: syntax error at or near "=" (SQLSTATE 42601)
[0.128ms] [rows:0] SELECT * FROM "posts" WHERE "posts". = 1 ORDER BY "posts". LIMIT 1
{0 {0 0 <nil>} 0 [] {0 0 <nil>}}
The nature of this error suggests to me that the primary_key value is not set. I tried primaryKey and primarykey, but none appeared to work. Any ideas?
#Shubham Srivastava - Your structure declaration is bad; you need to export fields so gorm can use them Declaring Models
❗️Use exported ID field
In Go, when a field name starts with a lowercase letter that means the field is private (called unexported in Go parlance). Gorm, being a third party package, cannot see inside your struct to know there is an id field, or any other unexported field for that matter.
The solution is to make sure all the fields that need to come from the DB are exported:
type Post struct {
ID uint `json:"id" gorm:"primaryKey"`
...
}

Error - redigo.Scan: Cannot convert from Redis bulk string to *string

I have a struct like that
type User struct {
Nickname *string `json:"nickname"`
Phone *string `json:"phone"`
}
Values ​​are placed in redis with HMSET command. (values ​​can be nil)
Now I'm trying to scan values ​​into a structure:
values, err := redis.Values(Cache.Do("HMGET", "key", "nickname", "phone" )
var usr User
_, err := redis.Scan(values, &usr.Nickname, &usr.Phone)
But I get an error
redigo.Scan: cannot assign to dest 0: cannot convert from Redis bulk
string to *string
Please tell me what I'm doing wrong?
The Scan documentation says:
The values pointed at by dest must be an integer, float, boolean, string, []byte, interface{} or slices of these types.
The application passes a pointer to a *string to the function. A *string is not one of the supported types.
There are two approaches for fixing the problem. The first is to allocate string values and pass pointers to the allocated string values to Scan:
usr := User{Nickname: new(string), Phone: new(string)}
_, err := redis.Scan(values, usr.Nickname, usr.Phone)
The second approach is to change the type of the struct fields to string:
type User struct {
Nickname string `json:"nickname"`
Phone string `json:"phone"`
}
...
var usr User
_, err := redis.Scan(values, &usr.Nickname, &usr.Phone)
From the doc it says that []byte is type for bulk string, not *string. You have two options here:
change the particular field type to []byte
or use temporary variable with []byte type on the scan, then after the data retrieved store it to the struct's field

Get length of nullable string

I have the following structure:
type NetAuth struct {
Identificator *string `json:"identificator"`
Password *string `json:"password"`
DeviceID *string `json:"deviceId"`
Type int `json:"type"`
}
I am trying to get the length of Identificator with len(event.Identificator) however I get Invalid argument for len
Before calling len I check if it's nil. I am coming from Java/C#/PHP background and it's my first time writing in GO.
There is no simple len(string) concept in Go. You either need the number of bytes in the string representation or the number of characters (so called runes). For ASCII strings both values are the same while for unicode-encoded strings they are usually different.
import "unicode/utf8"
// Firt check that the pointer to your string is not nil
if nil != event.Identificator {
// For number of runes:
utf8.RuneCountInString(*event.Identificator)
// For number of bytes:
len(*event.Identificator)
}
For more information you can check this answer https://stackoverflow.com/a/12668840/1201488.
UPD: event.Identificator is a pointer to a string value in the NetAuth structure rather than a string value. So you need to dereference it first via *event.Identificator.
You are using a pointer, so try this:
println(len(*vlr.Identificator))
For example,
package main
import (
//"fmt"
"encoding/json"
"strings"
"io"
)
type NetAuth struct {
Identificator *string `json:"identificator"`
Password *string `json:"password"`
DeviceID *string `json:"deviceId"`
Type int `json:"type"`
}
func jsondata() io.Reader {
return strings.NewReader(`{"identificator": "0001", "password": "passkey"}`)
}
func main() {
dec := json.NewDecoder(jsondata())
vlr := new(NetAuth)
dec.Decode(vlr)
println(*vlr.Identificator)
println(len(*vlr.Identificator))
}
Playground: https://play.golang.org/p/duf0tEddBsR
Output:
0001
4

Assign pointer to variable in Golang

I am working in Golang, I have a Struct that contains some fields, one of them is a time.Time field to save the Delete_Atso it can be null, basically I have defined it as:
type Contact struct {
Delete_At *time.Time
}
So, with the pointer it can be null. Then, I have a method when this value is assigned, I have something like:
contact := Contact{}
contact.Deleted_At = time.Now()
But with it, I get:
cannot use time.Now() (type time.Time) as type *time.Time in assignment
I totally understand that is a bad assignment, but, how should I do it? how the conversion should be done?
t := time.Now()
contact.Deleted_At = &t
And BTW, you should not use _ in variable names. DeletedAt would be the recommended name.

Could't convert <nil> into type ...?

I tried to using database/sql for query database row into a Go type, my codes snippet following:
type User struct {
user_id int64
user_name string
user_mobile string
password string
email interface{}
nickname string
level byte
locked bool
create_time string
comment string // convert <nil> to *string error
}
func TestQueryUser(t *testing.T) {
db := QueryUser(driverName, dataSourceName)
stmtResults, err := db.Prepare(queryAll)
defer stmtResults.Close()
var r *User = new(User)
arr := []interface{}{
&r.user_id, &r.user_name, &r.user_mobile, &r.password, &r.email,
&r.nickname, &r.level, &r.locked, &r.create_time, &r.comment,
}
err = stmtResults.QueryRow(username).Scan(arr...)
if err != nil {
t.Error(err.Error())
}
fmt.Println(r.email)
}
MySQL:
As your see, some fields that has NULL value, so I have to set interface{} type into User struct of Go, which convert NULL to nil.
--- FAIL: TestQueryUser (0.00s)
user_test.go:48: sql: Scan error on column index 9: unsupported Scan, storing driver.Value type <nil> into type *string
Somebody has a better way? or I must change the MySQL field and set its DEFAULT ' '
First the short answer : There is some types in sql package for example sql.NullString (for nullable string in your table and guess Nullint64 and NullBool and ... usage :) ) and you should use them in your struct.
The long one : There is two interface for this available in go , first is Scanner and the other is Valuer for any special type in database, (for example,I use this mostly with JSONB in postgres) you need to create a type, and implement this two(or one of them) interface on that type.
the scanner is used when you call Scan function. the data from the database driver, normally in []byte is the input and you are responsible for handling it. the other one, is used when the value is used as input in query. the result "normally" is a slice of byte (and an error) if you need to only read data, Scanner is enough, and vice-versa, if you need to write parameter in query the Valuer is enough
for an example of implementation, I recommend to see the types in sql package.
Also there is an example of a type to use with JSONB/JSON type in postgresql
// GenericJSONField is used to handle generic json data in postgres
type GenericJSONField map[string]interface{}
// Scan convert the json field into our type
func (v *GenericJSONField) Scan(src interface{}) error {
var b []byte
switch src.(type) {
case []byte:
b = src.([]byte)
case string:
b = []byte(src.(string))
case nil:
b = make([]byte, 0)
default:
return errors.New("unsupported type")
}
return json.Unmarshal(b, v)
}
// Value try to get the string slice representation in database
func (v GenericJSONField) Value() (driver.Value, error) {
return json.Marshal(v)
}
driver value is often []byte but string and nil is acceptable. so this could handle null-able fields too.

Resources