Golang Gorm working with slices and postgres' jsob field - json

I have a requirement to save either [] or a list with different integer values like [1, 7, 8]. These values can be anything between 1-31.
My struct for this field (DateOfMonth) is:
type Subscription struct {
gorm.Model
Enabled bool `gorm:"DEFAULT:True"`
Deleted bool `gorm:"DEFAULT:False"`
UserID uint `gorm:"not null"`
Cap int `gorm:"DEFAULT:-1"`
DateOfMonth []int64 `gorm:"type:json default '[]'::json"`
}
Now, I need to read this value in an API and compare it with the current_date.
For this, I have tried:
type Result struct {
ID uint
Email string
UniqueIdentifier string
Cap int
DateOfMonth []uint8
}
var subscriptions []Result
if err := db.Table("users").Select("users.id, users.email, users.unique_identifier, subscriptions.cap, subscriptions.date_of_month").Joins("join subscriptions on users.id = subscriptions.user_id").Where("subscriptions.subscription_type_id=? and users.is_verified=? and subscriptions.enabled=?", subscription_type_id, true, true).Find(&subscriptions).Error; err != nil {
c.JSON(http.StatusNotFound, gin.H{"error": true, "reason": "Subscribers not found!", "code": http.StatusBadRequest, "status": "failure"})
return
}
If I change DateOfMonth []uint8 to DateOfMonth []int64, it gives error.
The value that I receive in this field is a list of byte values
For example, [] -> [91 93] and [6] -> [91 54 93]
If I do, bytes.NewBuffer(s.DateOfMonth), I get the correct value but then I need to iterate over this slice to compare it with today's date. I have tried a lot of ways to get the actual value (6) in the loop (dom value) but to no avail.
// if len(s.DateOfMonth) > 0 {
// // date_of_month_new := binary.BigEndian.Uint64(date_of_month)
// todays_date_of_month := time.Now().Day()
// fmt.Println(todays_date_of_month) //, date_of_month, reflect.TypeOf(date_of_month))
// for _, dom := range s.DateOfMonth {
// fmt.Println("help", reflect.TypeOf(dom), dom, todays_date_of_month)
// // if dom == todays_date_of_month {
// // fmt.Println("matching", dom, todays_date_of_month)
// // }
// }
// }
I have even tried suggestions from various answers like this, this, this
What am I missing here? Your help will be highly appreciated.
Some of the errors that I got:
invalid sql type DateOfMonth (slice) for postgres
Golang cannot range over pointer to slice
cannot range over bytes.NewBuffer(s.DateOfMonth) (type *bytes.Buffer)
sql: Scan error on column index 4, name "date_of_month": unsupported Scan, storing driver.Value type []uint8 into type *[]int

Golang cannot range over pointer to slice
You are iterating over a pointer to a slice, instead of a slice. This means you will have to first de-reference your variable and then loop over it. Check out this example.
cannot range over bytes.NewBuffer(s.DateOfMonth) (type *bytes.Buffer)
You cannot range over type *bytes.Buffer. You can instead access the bytes of the type by using the method Buffer.Bytes(). Check out this example.
sql: Scan error on column index 4, name "date_of_month": unsupported Scan, storing driver.Value type []uint8 into type *[]int
Judging by the error I'm guessing this happens when you use type []int64 while scanning DateOfMonth. One of the possibilities for this error is your database storing the values as []uint8 instead of []int64.
invalid sql type DateOfMonth (slice) for postgres
I'll try and update my answer after I am able to reproduce this error successfully.

Related

Scan error: unsupported Scan, storing driver.Value type <nil> into type *string

When I try to fetch Articles without photo from mysql database:
func ArticlesAllText() ([]Article, error) {
var err error
var result []Article
err = database.SQL.Select(&result, "SELECT * FROM Article WHERE photo IS NULL")
if err != nil {
log.Println(err)
}
return result, standardizeError(err)
}
I get
sql: Scan error on column index 10: unsupported Scan, storing
driver.Value type into type *string
When the field value is NULL.
How can I fix this?
Use sql.NullString.
https://godoc.org/database/sql#NullString
Personally, I hate this solution, which is why I normalize my DB so that there are no NULLs.
Either change
var result []Article
To
var result []sql.NullString
Then take those results and make a []Article using the checks from the documentation.
Or in your struct, change
Photo *string
To
Photo sql.NullString
And deal with the annoyance of it being a struct instead of a *string
Thanks to ATN, see here for a guide https://medium.com/aubergine-solutions/how-i-handled-null-possible-values-from-database-rows-in-golang-521fb0ee267
You can use any of the below two solutions:-
You can use sql.NullString to handle the field before using scan(). OR
You can replace all the possible NULL values with the desired string say '' from the query itself.
For implementing the 1st solution refer to the #RayfenWindspear answer.
For the 2nd solution update the query as below:-
SELECT colm1, colm2, COALESCE(photo, '') photo, colm4 FROM Article WHERE photo IS NULL
For MySQL use IFNULL() or COALESCE() function to return an alternative value if an expression is NULL:
For SQL Server use IFNULL() or COALESCE() function for the same result
MS Access use IsNull function for the same result
For Oracle use NVL() function for the same result
Reference: https://www.w3schools.com/sql/sql_isnull.asp

How to detect a zeroed value of struct safely in Go?

I tried to written an simple ORM for MySQL, I got a problem, if a defined struct missing some field:
type User struct {
ID int64
Username string
Password string
Email string
Comment string
}
var u = User{Username: "user_0001", Password: "password"}
Some fields of User didn't given a value, then it's value will be a zeroed value, such as string "", bool false, integer 0 and so on.
So I am using reflect to get field name and value, generate a sql to insert row.
INSERT INTO User (Id, Username, Password, Email, Comment) Values (0, "user_0001", "password", , ,)
You can see there has some zeroed value of string, if I detect empty string "" and skip them, I may skip normal value.
To handle database columns that may be NULL, you can either use pointers as Friedrich Große suggests, or use the Null...-variants found in the db package, for instance sql.NullString.
To use the Null-variants, the struct would be
type User struct {
ID int64
Username string
Password string
Email sql.NullString
Comment sql.NullString
}
You can then detect of a value is set by checking NullString.Valid. The downside of using NullString is that you have to add special cases when printing or marshaling them, as they don't implement the Stringer interface, nor MarshalJSON or UnmarshalJSON. You also have to remember to set NullString.Valid manually when setting the value of the string.
For instance, a test like
func TestNullString(t *testing.T) {
s := sql.NullString{}
t.Log(s)
s.String = "Test"
s.Valid = true
t.Log(s)
}
Prints
null_string_test.go:21: { false}
null_string_test.go:25: {Test true}
To print a NullString you have to instead do
func TestNullString(t *testing.T) {
s := sql.NullString{}
t.Log(s.String)
s.String = "Test"
s.Value = true
t.Log(s.String)
}
Which prints:
null_string_test.go:21:
null_string_test.go:25: Test
You can always just insert an empty string into your table column. If it is truly important to know the difference between the zero value or complete absence of a value you would need to use pointers.
type User struct {
ID int64
Username *string
}
This changes the zero value to be nil so you can distinguish that from "".
The downside is that this makes this type less easy to use (nil checks are often forgotten in practice) and you have to dereference that field to get the actual string.
In your specific example I don't see why you need to worry about the empty value at all. Can't you just insert "" into the database and enforce validation (non-emptiness) in your code instead of using database constraints?

Golang set a maps key as a variable for its value

I need to go through an html forms input values and place them into an mysql datbase. Which I'm currently using r.Form to get a map. So that i don't have to use r.Form.Get("date") for each which works but when i try to put the values into a database. It compiles just fine but i get sql: converting argument #0's type: unsupported type []string, a slice after i click submit in the browser. I can get around this by doing
`date := strings.Join(m["date"], "")`
but doing that for 30+ values especially since some of the submited values will be created from previous database entries using html templates. If i have to change or add more later seems like there must be a more efficient way I've seen for key, val := range m {} but unfortunately I've only been doing this for about a week and i can't figure out how to keep the values and change the variable they get set to after each iteration. So that after
for key, val := range m {
x := m[key]
}
so that it will put out the equivalent
keyname := keyvalue
changing the keyname each time to be the same as the keyname in the map ie
date := 2015-8-13
time := 18:56:11
or if there's an easier way around this error then to create a varible for each one.
An HTML form can have multiple values for a single key. This is why the request form field is defined to be a map of string slices. The request Form is declared as
Form url.Values
and url.Values is declared as
type Values map[string][]string
You can access the first value for a key using:
var value string
if values := req.Form[key]; len(values) > 0 {
value = values[0]
}
The url.Values Get helper method simplifies this code to:
value := req.Form.Get(key)
The http.Request FormValue helper method simplifies it a bit more:
value := req.FormValue(key)
You iterate through keys and values using:
for key, values := range req.Form {
for _, value := range values {
fmt.Println(key, value)
}
}
If you want to iterate over the first value for a key, then use this code:
for key, values := range req.Form {
if len(values) > 0 {
value := values[0]
fmt.Println(key, value)
}
}
Before accessing req.Form, call req.ParseForm to parse the query string and request body.

golang return multiple values issue

I was wondering why this is valid go code:
func FindUserInfo(id string) (Info, bool) {
it, present := all[id]
return it, present
}
but this isn't
func FindUserInfo(id string) (Info, bool) {
return all[id]
}
is there a way to avoid the temporary variables?
To elaborate on my comment, the Effective Go mentions that the multi-value assignment from accessing a map key is called the "comma ok" pattern.
Sometimes you need to distinguish a missing entry from a zero value. Is there an entry for "UTC" or is that the empty string because it's not in the map at all? You can discriminate with a form of multiple assignment.
var seconds int
var ok bool
seconds, ok = timeZone[tz]
For obvious reasons this is called the “comma ok” idiom. In this example, if tz is present, seconds will be set appropriately and ok will be true; if not, seconds will be set to zero and ok will be false.
Playground demonstrating this
We can see that this differs from calling a regular function where the compiler would tell you that something is wrong:
package main
import "fmt"
func multiValueReturn() (int, int) {
return 0, 0
}
func main() {
fmt.Println(multiValueReturn)
asgn1, _ := multiValueReturn()
asgn2 := multiValueReturn()
}
On the playground this will output
# command-line-arguments
/tmp/sandbox592492597/main.go:14: multiple-value multiValueReturn() in single-value context
This gives us a hint that it may be something the compiler is doing. Searching the source code for "commaOk" gives us a few places to look, including types.unpack
At the time of writing this it this the method's godoc reads:
// unpack takes a getter get and a number of operands n. If n == 1, unpack
// calls the incoming getter for the first operand. If that operand is
// invalid, unpack returns (nil, 0, false). Otherwise, if that operand is a
// function call, or a comma-ok expression and allowCommaOk is set, the result
// is a new getter and operand count providing access to the function results,
// or comma-ok values, respectively. The third result value reports if it
// is indeed the comma-ok case. In all other cases, the incoming getter and
// operand count are returned unchanged, and the third result value is false.
//
// In other words, if there's exactly one operand that - after type-checking
// by calling get - stands for multiple operands, the resulting getter provides
// access to those operands instead.
//
// If the returned getter is called at most once for a given operand index i
// (including i == 0), that operand is guaranteed to cause only one call of
// the incoming getter with that i.
//
The key bits of this being that this method appears to determine whether or not something is actually a "comma ok" case.
Digging into that method tells us that it will check to see if the mode of the operands is indexing a map or if the mode is set to commaok (where this is defined does give us many hints on when it's used, but searching the source for assignments to commaok we can see it's used when getting a value from a channel and type assertions). Remember the bolded bit for later!
if x0.mode == mapindex || x0.mode == commaok {
// comma-ok value
if allowCommaOk {
a := [2]Type{x0.typ, Typ[UntypedBool]}
return func(x *operand, i int) {
x.mode = value
x.expr = x0.expr
x.typ = a[i]
}, 2, true
}
x0.mode = value
}
allowCommaOk is a parameter to the function. Checking out where unpack is called in that file we can see that all callers pass false as an argument. Searching the rest of the repository leads us to assignments.go in the Checker.initVars() method.
l := len(lhs)
get, r, commaOk := unpack(func(x *operand, i int) { check.expr(x, rhs[i]) }, len(rhs), l == 2 && !returnPos.IsValid())
Since it seems that we can only use the "comma ok" pattern to get two return values when doing a multi-value assignment this seems like the right place to look! In the above code the length of the left hand side is checked, and when unpack is called the allowCommaOk parameter is the result of l == 2 && !returnPos.IsValid(). The !returnPos.IsValid() is somewhat confusing here as that would mean that the position has no file or line information associated with it, but we'll just ignore that.
Further down in that method we've got:
var x operand
if commaOk {
var a [2]Type
for i := range a {
get(&x, i)
a[i] = check.initVar(lhs[i], &x, returnPos.IsValid())
}
check.recordCommaOkTypes(rhs[0], a)
return
}
So what does all of this tell us?
Since the unpack method takes an allowCommaOk parameter that's hardcoded to false everywhere except in assignment.go's Checker.initVars() method, we can probably assume that you will only ever get two values when doing an assignment and have two variables on the left-hand side.
The unpack method will determine whether or not you actually do get an ok value in return by checking if you are indexing a slice, grabbing a value from a channel, or doing a type assertion
Since you can only get the ok value when doing an assignment it looks like in your specific case you will always need to use variables
You may save a couple of key strokes by using named returns:
func FindUserInfo(id string) (i Info, ok bool) {
i, ok = all[id]
return
}
But apart from that, I don't think what you want is possible.
Simply put: the reason why your second example isn't valid Go code is because the language specification says so. ;)
Indexing a map only yields a secondary value in an assignment to two variables. Return statement is not an assignment.
An index expression on a map a of type map[K]V used in an assignment or initialization of the special form
v, ok = a[x]
v, ok := a[x]
var v, ok = a[x]
yields an additional untyped boolean value. The value of ok is true if the key x is present in the map, and false otherwise.
Furthermore, indexing a map is not a "single call to a multi-valued function", which is one of the three ways to return values from a function (the second one, the other two not being relevant here):
There are three ways to return values from a function with a result type:
The return value or values may be explicitly listed in the "return" statement. Each expression must be single-valued and assignable to the corresponding element of the function's result type.
The expression list in the "return" statement may be a single call to a multi-valued function. The effect is as if each value returned from that function were assigned to a temporary variable with the type of the respective value, followed by a "return" statement listing these variables, at which point the rules of the previous case apply.
The expression list may be empty if the function's result type specifies names for its result parameters. The result parameters act as ordinary local variables and the function may assign values to them as necessary. The "return" statement returns the values of these variables.
As for your actual question: the only way to avoid temporary variables would be using non-temporary variables, but usually that would be quite unwise - and probably not much of an optimization even when safe.
So, why doesn't the language specification allow this kind of special use of map indexing (or type assertion or channel receive, both of which can also utilize the "comma ok" idiom) in return statements? That's a good question. My guess: to keep the language specification simple.
I'm no Go expert but I believe you are getting compile time error when you are trying to return the array i.e. return all[id]. The reason could be because the functions return type is specially mentioned as (Info, bool) and when you are doing return all[id] it can't map the return type of all[id] to (Info, bool).
However the solution mentioned above, the variables being returned i and ok are the same that are mentioned in the return type of the function (i Info, ok bool) and hence the compiler knows what it's returning as opposed to just doing (i Info, ok bool).
By default, maps in golang return a single value when accessing a key
https://blog.golang.org/go-maps-in-action
Hence, return all[id] won't compile for a function that expects 2 return values.

C MySQL Types Error

I'm trying to store results taken from a MySQL query into an array of structs. I can't seem to get the types to work though, and I've found the MySQL documentation difficult to sort through.
My struct is:
struct login_session
{
char* user[10];
time_t time;
int length;
};
And the loop where I'm trying to get the data is:
while ( (row = mysql_fetch_row(res)) != NULL ) {
strcpy(records[cnt].user, &row[0]);
cnt++;
}
No matter what I try though I constantly get the error:
test.c:45: warning: passing argument 1 of ‘strcpy’ from incompatible pointer type
/usr/include/string.h:128: note: expected ‘char * __restrict__’ but argument is of type ‘char **’
test.c:45: warning: passing argument 2 of ‘strcpy’ from incompatible pointer type
/usr/include/string.h:128: note: expected ‘const char * __restrict__’ but argument is of type ‘MYSQL_ROW’
Any pointers?
Multiple problems, all related to pointers and arrays, I recommend you do some reading.
First, char * user[10] is defining an array of 10 char * values, not an array of char, which is was I suspect you want. The warning even says as much, strcpy() expects a char *, the user field on its own is seen as a char **.
Second, you're one & away from what you want in the second argument.
Copied from mysql.h header:
typedef char **MYSQL_ROW; /* return data as array of strings */
A MYSQL_ROW is an array of char arrays. Using [] does a dereference, so you dereference down to a char * which is what strcpy() takes, but then you take the address of it using &.
Your code should look more like this:
struct login_session
{
char user[10];
time_t time;
int length;
};
while ( (row = mysql_fetch_row(res)) != NULL ) {
strcpy(records[cnt].user, row[0]);
cnt++;
}
I don't know what guarantees you have about the data coming from mysql, but if you can't be absolutely sure that the rows are <= 10 characters long and null ('\0') terminated, you should use strncpy() to avoid any possibility of overflowing the user array.