Golang set a maps key as a variable for its value - mysql

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.

Related

Golang Gorm working with slices and postgres' jsob field

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.

Dynamic SQL select query in Golang

I am trying to build API, with database/sql and mysql driver, that will read data based on URL parameters.
Something like this
myapi.com/users?columns=id,first_name,last_name,country&sort=desc&sortColumn=last_name&limit=10&offset=20
I know how to get all columns or just specific columns when it is defined in struct. But I want to know is it possible to get columns from url and instead of predefined struct save it to map and than just scan those columns.
I have working code that will get data from above endpoint only if number of columns is same as in struct. If I remove country for example I get error that Scan expects 4 params but 3 are given.
I don't need specific code, just some directions since I am learning Go and my background is PHP where this is easier to do.
Update
Thanks to answers I have partly working solution.
Here is code:
cols := []string{"id", "first_name", "last_name"}
vals := make([]interface{}, len(cols))
w := map[string]interface{}{"id": 105}
var whereVal []interface{}
var whereCol []string
for k, v := range w {
whereVal = append(whereVal, v)
whereCol = append(whereCol, fmt.Sprintf("%s = ?", k))
}
for i := range cols {
vals[i] = new(interface{})
}
err := db.QueryRow("SELECT "+strings.Join(cols, ",")+" FROM users WHERE "+strings.Join(whereCol, " AND "), whereVal...).Scan(vals...)
if err != nil {
fmt.Println(err)
}
b, _ := json.Marshal(vals)
fmt.Println(string(b))
This should query SELECT id, first_name, last_name FROM users WHERE id = 105;
But how do I get data out to proper json object? Now it prints out strings encoded in base64 like this.
[105,"Sm9obm55","QnJhdm8="]
From what I know (also not much experienced in Go) if you don't assign a real type to value then Scan will return []byte and when it is marshalled it returns base64 encoded string.
So you have to assign a type to your columns and if you want proper json then assign keys to values.
In your example it can be done something like this:
cols := []string{"id", "first_name", "last_name"}
vals := make([]interface{}, len(cols))
result := make(map[string]interface{}, len(cols))
for i, key := range cols {
switch key {
case "id", "status":
vals[i] = new(int)
default:
vals[i] = new(string)
}
result[key] = vals[i]
}
b, _ := json.Marshal(result)
fmt.Println(string(b))
So, instead of looping over cols and creating new interface for each column, now we are creating key/value pairs and assigning type based on column name.
Also, if you have nullable columns in table, and you probably have, then you'll get error because nil can't go into string. So I suggest this package gopkg.in/guregu/null.v3 and then assign type like null.String. That way you'll get back null as a value.
For example:
for i, key := range cols {
switch key {
case "id", "status":
vals[i] = new(int)
case "updated_at", "created_at":
vals[i] = new(null.Time)
default:
vals[i] = new(null.String)
}
result[key] = vals[i]
}
Here is an option that a found to return dynamic resultset, you will need a interface{} array but you have to assign to a new(interface{}) to get a pointer that can be write by Scan method
//...
types, _ := rows.ColumnTypes()
for rows.Next() {
row := make([]interface{}, len(types))
for i := range types {
row[i] = new(interface{})
}
rows.Scan(row...)
}
You must first fetch the result columns count and then don't exceed the size.
If you meant the query fields, you need dynamic create the query string, the params size must be the same.
I would create a query statement with the dynamic fields(use placeholder for avoiding sql injection):
rows := db.QueryRow("SELECT {{YOUR_FIELDS}} from table_tbl")
Create variable carrier with the same size of columns
vals := make([]interface{}, len(rows.Columns()))
Use sql.RawBytes for field's type if you don't need type checking or can't know their types, otherwise use the same type of field.
for i, _ := range cols {
vals[i] = new(sql.RawBytes)
//check column name, if it is id, and you know it is integer
//vals[i] = new(int)
}
Iterate rows and scan
for rows.Next() {
err = rows.Scan(vals...)
}

Fetch records with query Args in Go

I Need help for fetch records from table using Go.
My Problem is that i'm writing MySQL query and add another where clause i.e HPhone number, Here HPhone number inserted in data base with format like 999-999-9999.And i passed this HPhone Number in format like 9999999999. which is not matching with correct data base field value. And i used SUBSTRING for add hyphen between numbers but it does not get records but when i passed like 999-999-9999 without SUBSTRING it return records.
Here i demonstrate how i used this.
strQry = `SELECT * from table WHERE Depot = ?`
if HPhone != "" {
strQry += ` AND HPhone = ?`
}
queryArgs := []interface{}{RouteAvailability.Depot}
if HPhone != "" {
queryArgs = append(queryArgs, "SUBSTRING("+HPhone+",1,3)"+"-"+"SUBSTRING("+HPhone+",4,3)"+"-"+"SUBSTRING("+HPhone+",7,4)")
}
Help would be appreciated.
Thanks in advance.
Instead of SUBSTRING you can use REPLACE like so:
queryArgs := []interface{}{RouteAvailability.Depot}
if HPhone != "" {
strQry += ` AND REPLACE(HPhone, '-', '') = ?`
queryArgs = append(queryArgs, HPhone)
}
If possible I would suggest you normalize your data, i.e. decide on a canonical format for a particular data type and everytime your program receives some input that contains that data type you format it into its canonical form, that way you can avoid having to deal with SUBSTRING, or REPLACE, or multiple inconsistent formats etc.
This won't work as you are using prepared statements, and the argument you are building when HPhone is not empty will be used in escaped form - so when executing the query, it won't compare the HPhone values with the computed result of some substring, but with a string containing SUBSTRING(9999...

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?

PL/JSON get both the value and the parameter in json list

I need to get both the value and the parameter in a json list using pl/json library. For an example,
{TABLE:"TEST",Parameters:[{"PID":"bar"},{"PFOJ":"baz"},{"PCLI":"bar"}]}
I need to get the parameter and the corresponding value in the 'Prameters' list in this json object like,
param varchar2(20);
value varchar2(20);
param := get_param(1); //from Parameters list
value := get_value(1); //from Parameters list
Thank you!
You need to get the key name:
varchar2 key := parameter_obj.get_keys.get(1).get_string();