Related
I have an application in go, and there is an endpoint for editing user information. But I found a problem, if in json it sees blank information (let's assume the user just wants to edit the name, and leaves the other fields blank), the data will be edited in the DB and the information that came blank will overwrite the information that are DB, even if the user didn't want to edit them.
How can I make only the fields that arrive in json to be effectively edited in the DB? Or is there a better way?
I'm looking forward to knowing!
My controller
func EditUser(w http.ResponseWriter, r *http.Request) {
params := mux.Vars(r)
userID, err := strconv.ParseUint(params["userID"], 10, 64)
if err != nil {
returns.ERROR(w, http.StatusInternalServerError, err)
return
}
userIDInToken, err := auth.ExtractUserID(r)
if err != nil {
returns.ERROR(w, http.StatusInternalServerError, err)
return
}
if userIDInToken != userID {
returns.ERROR(w, http.StatusForbidden, errors.New("you can't update other user"))
return
}
bodyRequest, err := ioutil.ReadAll(r.Body)
if err != nil {
returns.ERROR(w, http.StatusBadRequest, err)
return
}
var user models.User
if err := json.Unmarshal(bodyRequest, &user); err != nil {
returns.ERROR(w, http.StatusUnprocessableEntity, err)
return
}
db, err := db.ConnectToDB()
if err != nil {
returns.ERROR(w, http.StatusInternalServerError, err)
return
}
defer db.Close()
repository := repositories.NewUsersRepository(db)
if err := repository.UpdateUserInfo(userID, user); err != nil {
returns.ERROR(w, http.StatusInternalServerError, err)
return
}
returns.JSON_RESPONSE(w, http.StatusOK, nil)
}
My repository (that access DB)
func (repository Users) UpdateUserInfo(userID uint64, user models.User) error {
stmt, err := repository.db.Prepare(
"UPDATE user SET name = ?, cpf = ?, email = ?, password = ?, city = ?, state = ? WHERE id = ?")
if err != nil {
return err
}
defer stmt.Close()
if _, err := stmt.Exec(
user.Name,
user.CPF,
user.Email,
user.Password,
user.City,
user.State,
userID,
); err != nil {
return err
}
return nil
}
Model for user
type User struct {
ID uint64 `json:"id,omitempty"`
Name string `json:"name,omitempty"`
CPF string `json:"cpf,omitempty"`
Email string `json:"email,omitempty"`
Password string `json:"password,omitempty"`
City string `json:"city,omitempty"`
State string `json:"state,omitempty"`
}
I would dynamically construct the UPDATE statement as well as a slice of the fields that need to be edited by checking if the field is not empty before adding it to the slice.
Something like this:
func (repository Users) UpdateUserInfo(userID uint64, user User) error {
fields := make([]string, 0)
values := make([]string, 0)
if user.Name != "" {
values = append(values, user.Name)
fields = append(fields, "name = ?")
}
if user.CPF != "" {
values = append(values, user.CPF)
fields = append(fields, "cpf = ?")
}
if user.Email != "" {
values = append(values, user.Email)
fields = append(fields, "email = ?")
}
if user.Password != "" {
values = append(values, user.Password)
fields = append(fields, "password = ?")
}
if user.City != "" {
values = append(values, user.City)
fields = append(fields, "city = ?")
}
if user.State != "" {
values = append(values, user.State)
fields = append(fields, "state = ?")
}
if len(fields) != 0 {
return errors.New("no fields to update")
}
updateString := fmt.Sprintf("UPDATE user SET %s WHERE id = ?", strings.Join(fields, ","))
stmt, err := repository.db.Prepare(updateString)
if err != nil {
return err
}
defer stmt.Close()
if _, err := stmt.Exec(values...,userID); err != nil {
return err
}
return nil
}
For cleaner code, I'd recommend maybe extracting the "if statements"/validation into a separate function.
Following is my code to get multiple rows from db and it works.
defer db.Close()
for rows.Next() {
err = rows.Scan(&a)
if err != nil {
log(err)
}
How can I check if the rows contains No row?
Even I tried like below
if err == sql.ErrNoRows {
fmt.Print(No rows)
}
and also checked while scanning
err = rows.Scan(&a)
if err == sql.ErrNoRows {
fmt.Print(No rows)
}
I don't understand which one gives ErrNoRows either *Rows or err or Scan
QueryRow returns a *Row (not a *Rows) and you cannot iterate through the results (because it's only expecting a single row back). This means that rows.Scan in your example code will not compile).
If you expect your SQL query to return a single resullt (e.g. you are running a count() or selecting using an ID) then use QueryRow; for example (modified from here):
id := 43
var username string
err = stmt.QueryRow("SELECT username FROM users WHERE id = ?", id).Scan(&username)
switch {
case err == sql.ErrNoRows:
log.Fatalf("no user with id %d", id)
case err != nil:
log.Fatal(err)
default:
log.Printf("username is %s\n", username)
}
If you are expecting multiple rows then use Query() for example (modified from here):
age := 27
rows, err := db.Query("SELECT name FROM users WHERE age=?", age)
if err != nil {
log.Fatal(err)
}
defer rows.Close()
names := make([]string, 0)
for rows.Next() {
var name string
if err := rows.Scan(&name); err != nil {
log.Fatal(err)
}
names = append(names, name)
}
// Check for errors from iterating over rows.
if err := rows.Err(); err != nil {
log.Fatal(err)
}
// Check for no results
if len(names) == 0 {
log.Fatal("No Results")
}
log.Printf("%s are %d years old", strings.Join(names, ", "), age)
The above shows one way of checking if there are no results. If you are not putting the results into a slice/map then you can keep a counter or set a boolean within the loop. Note that no error will be returned if there are no results (because this is a perfectly valid outcome) and the SQL package provides no way to check the number of results other than iterate through them (if all you are interested in is the number of results then run select count(*)...).
I am working on a function which gets users. My problem is that I have to return the result of two queries as one result in the rows.Next() and rows1.Next() like user_id, subject, phone.
Here is what I have but it doesn't work. Could you please help me out with some suggestions:
func GetUsers() (users []Users, err error) {
users = make([]Users, 0)
rows, err := db1.Query("SELECT user_id, subject,phone FROM users limit 11")
rows1, err := db1.Query("Select body from users limit 11")
defer rows.Close()
if err != nil {
// handle this error better than this
log.Fatal(err)
//return
}
var user Users
for rows.Next() {
rows.Scan(&user.ID, &user.Subject, &user.Phone)
users = append(users, user)
}
for rows1.Next() {
rows1.Scan(&user.Body)
users = append(users, user)
}
return
}
I have a table called users which has no primary key, basically:
user_id . name . body
-----------------------
1 . model . 2
1 . brand . 1
1 . fuel . 3
1 . date . 1
1 . year . 1
I have to have a result:
userid . model . brand . fuel . date . year
1 . 2 . 1 . 3 . 1 . 1
so the values in the column name gonna be as rows. I have already defined my struct which contains all the fields needed.
Looking at the example code it's clear that you should do whatever you're doing in one query instead of two. Even if your real code is a little different, say you need to query more than one table, you should still probably do only one query using JOINs.
users := make([]*User, 0)
rows, err := db1.Query("SELECT user_id, subject,phone, body FROM users limit 11")
if err != nil {
log.Fatal(err)
}
defer rows.Close()
for rows.Next() {
user := new(User)
if err := rows.Scan(&user.ID, &user.Subject, &user.Phone, &user.Body); err != nil {
panic(err)
}
users = append(users, user)
}
if err := rows.Err(); err != nil {
panic(err)
}
That said, if you want to do it the way you illustrated in your question you can do that like this.
(this probably not what you should do)
users := make([]*User, 0)
rows, err := db1.Query("SELECT user_id, subject,phone FROM users limit 11")
if err != nil {
log.Fatal(err)
}
defer rows.Close()
rows1, err := db1.Query("Select body from users limit 11")
if err != nil {
panic(err)
}
defer rows1.Close()
for rows.Next() {
user := new(User)
if err := rows.Scan(&user.ID, &user.Subject, &user.Phone); err != nil {
panic(err)
}
if !rows1.Next() {
panic("no next body row")
}
if err := rows1.Scan(&user.Body); err != nil {
panic(err)
}
users = append(users, user)
}
if err := rows.Err(); err != nil {
panic(err)
}
if err := rows1.Err(); err != nil {
panic(err)
}
const maxPerPage = 100
type Scanable interface {
GetScans() []interface{}
}
type User struct {
ID int
Subject string
Phone string
Body string
}
func (s *User) GetScans() []interface{} {
return []interface{}{&s.ID, &s.Subject, &s.Phone, &s.Body}
}
func getNewUserList() []*User {
users := make([]*User, maxPerPage)
for i := 0; i < maxPerPage; i++ {
users[i] = new(User)
}
return users
}
var usersPool = sync.Pool{
New: func() interface{} {
return getNewUserList()
},
}
func getUsers() (b []*User) {
ifc := usersPool.Get()
if ifc != nil {
b = ifc.([]*User)
} else {
b = getNewUserList()
}
return
}
func putUsers(b []*User) {
// if cap(b) <= maxPerPage {
// b = b[:0] // reset
// usersPool.Put(&b)
// }
usersPool.Put(&b)
}
func TestUsers(t *testing.T) {
users := getUsers()
rows, err := PostgresConnection.Query("SELECT user_id, subject,phone, body FROM users limit 11")
if err != nil {
log.Fatal(err)
}
defer rows.Close()
length := 0
for rows.Next() {
if err := rows.Scan(users[i].GetScans()...); err != nil {
panic(err)
}
if err := rows.Err(); err != nil {
panic(err)
}
length++
}
result := users[:length]
fmt.Println(result)
putUsers(users)
}
this is the sample function to get multiple document from db using golang sqlx library.
func GetDocuments() ([]SupportedDocument, error) {
var documents []SupportedDocument
query := `SELECT * FROM documents limit 10`
err := database.Select(&documents, query)
return documents, err
}
I am writing a function in Go to execute select query on database.
Input: String e.g. "Select id, name, age from sometable"
This query changes everytime.
Output: Output of select query in json format.
Sample Expected output: {"Data":[{"id":1,"name":"abc", "age":40},{"id":2,"name":"xyz", "age":45}]}
Sample Actual output: {"Data":[[1,"abc",40],[2,"xyz",45]]}
Instead of i.e. column_name:value, I get only values.
How do I get the expected output?
func executeSQL(queryStr string) []byte {
connString := createConnectString()
conn, err := sql.Open("mssql", connString)
if err != nil {
log.Fatal("Error while opening database connection:", err.Error())
}
defer conn.Close()
rows, err := conn.Query(queryStr)
if err != nil {
log.Fatal("Query failed:", err.Error())
}
defer rows.Close()
columns, _ := rows.Columns()
count := len(columns)
var v struct {
Data []interface{} // `json:"data"`
}
for rows.Next() {
values := make([]interface{}, count)
valuePtrs := make([]interface{}, count)
for i, _ := range columns {
valuePtrs[i] = &values[i]
}
if err := rows.Scan(valuePtrs...); err != nil {
log.Fatal(err)
}
v.Data = append(v.Data, values)
}
jsonMsg, err := json.Marshal(v)
return jsonMsg
}
Got the solution. Here is what I did.
func executeSQL(queryStr string) []byte {
connString := createConnectString()
conn, err := sql.Open("mssql", connString)
if err != nil {
log.Fatal("Error while opening database connection:", err.Error())
}
defer conn.Close()
rows, err := conn.Query(queryStr)
if err != nil {
log.Fatal("Query failed:", err.Error())
}
defer rows.Close()
columns, _ := rows.Columns()
count := len(columns)
var v struct {
Data []interface{} // `json:"data"`
}
for rows.Next() {
values := make([]interface{}, count)
valuePtrs := make([]interface{}, count)
for i, _ := range columns {
valuePtrs[i] = &values[i]
}
if err := rows.Scan(valuePtrs...); err != nil {
log.Fatal(err)
}
//Created a map to handle the issue
var m map[string]interface{}
m = make(map[string]interface{})
for i := range columns {
m[columns[i]] = values[i]
}
v.Data = append(v.Data, m)
}
jsonMsg, err := json.Marshal(v)
return jsonMsg
}
Let me know if there exists a better solution.
This code is directly from my "sandbox" for MsSQL (using denisenkom/go-mssqldb and jmoiron/sqlx) - I think it helps showing different approaches and probably QueryIntoMap is what you were looking for:
package main
import (
"log"
"fmt"
_ "github.com/denisenkom/go-mssqldb"
"time"
"github.com/jmoiron/sqlx"
"encoding/json"
)
type Customer struct {
CustomerId string `db:"customerID" json:"customer_id"`
Company interface{} `db:"companyName" json:"company_name"`
Contact interface{} `db:"contactName" json:"contact_name"`
}
func main() {
connection := "server=192.168.55.3\\SqlExpress2012;database=Northwind;user id=me;Password=secret"
//QueryIntoMap(connection)
ScanIntoSlice(connection)
}
func QueryIntoMap(connection string) {
fmt.Println("QueryIntoMap sample")
fmt.Println("--------------------")
sel := `select customerId, companyName, contactName
from customers
where customerId = :id`
values := make(map[string]interface{})
db, err := sqlx.Open("mssql", connection)
//db.MapperFunc(strings.ToUpper)
e(err)
defer db.Close()
tx := db.MustBegin()
stmt, err := tx.PrepareNamed(sel)
e(err)
stmt.QueryRowx(map[string]interface{}{"id": "BONAP"}).MapScan(values)
tx.Commit()
for k, v := range values {
fmt.Printf("%s %v\n", k, v)
}
js, err := json.Marshal(values)
if err != nil {
fmt.Println(err)
}
fmt.Println(string(js))
fmt.Println("--------------------")
}
func ScanIntoStruct(connection string) {
fmt.Println("Scan into struct sample")
fmt.Println("--------------------")
db, err := sqlx.Open("mssql", connection)
e(err)
defer db.Close()
customer := Customer{}
rows, err := db.Queryx(`select customerID, companyName, contactName
from Customers`)
for rows.Next() {
err = rows.StructScan(&customer)
if err != nil {
log.Fatalln(err)
}
//fmt.Printf("%#v\n", user)
fmt.Printf("%-10s %-50v %-50v\n",
customer.CustomerId,
customer.Company,
customer.Contact)
js, err := json.Marshal(customer)
e(err)
fmt.Println(string(js))
}
fmt.Println("--------------------")
}
func ScanIntoSlice(connection string) {
fmt.Println("Scan into slice sample")
fmt.Println("--------------------")
start := time.Now()
db, err := sqlx.Open("mssql", connection)
e(err)
defer db.Close()
customers := []Customer{}
err = db.Select(&customers, `select customerID, companyName, contactName from customers`)
e(err)
for i, customer := range customers {
fmt.Printf("%3d. %-10s %-50v %-50v\n",
i,
customer.CustomerId,
customer.Company,
customer.Contact)
}
js, err := json.Marshal(customers)
e(err)
fmt.Println(string(js))
fmt.Printf("%s", time.Since(start))
fmt.Println("--------------------")
}
func e(err error) {
if err != nil {
log.Fatal(err)
}
}
I'm try to understand mysql lock record mechanic, this is show case in Golang
or here https://play.golang.org/p/2fGKEyh0Wl
it run 2 concurrent transactions, and they read-update on the same row
- the first transaction will try to lock the row, do something (sleep for 3 secs)
- the second then try to read-update on the same key
the test source code
package main
import (
"github.com/jmoiron/sqlx"
"github.com/satori/go.uuid"
"log"
"sync"
"time"
_ "github.com/go-sql-driver/mysql"
)
type Wallet struct {
ID string
Balance int64
}
func main() {
db, err := sqlx.Connect("mysql", "root:abc123#tcp(mysql:3306)/test?parseTime=true")
if err != nil {
log.Println(err)
return
}
db.Exec(`CREATE TABLE test_wallet (
id varchar(64) NOT NULL,
balance bigint(20) DEFAULT NULL,
PRIMARY KEY (id)
) ENGINE=InnoDB DEFAULT CHARSET=utf8;
`)
var wg sync.WaitGroup
wg.Add(2)
wID := uuid.NewV4().String()
db.Exec("INSERT INTO test_wallet (id,balance) VALUES (?,?)", wID, 10)
go func() {
defer wg.Done()
tx, err := db.Beginx()
w1 := &Wallet{}
err = db.Get(w1, "SELECT * FROM test_wallet WHERE id=? FOR UPDATE", wID) // read and lock the record
if err != nil {
log.Println(err)
}
log.Printf("got %+v on r1\n", w1)
time.Sleep(time.Second * 3)
res, err := tx.Exec("UPDATE test_wallet SET balance=? WHERE id=?", w1.Balance+5, wID)
if err != nil {
log.Println(err)
}
n, err := res.RowsAffected()
if n != 1 {
log.Println("update not affected r1")
}
tx.Commit()
log.Println("done on r1")
}()
time.Sleep(time.Second) // make sure go-routine lock `id` row
go func() {
defer wg.Done()
tx, err := db.Beginx()
w2 := &Wallet{}
err = db.Get(w2, "SELECT * FROM test_wallet WHERE id=? FOR UPDATE", wID)
if err != nil {
log.Println(err)
}
log.Printf("got %+v on r2\n", w2)
res, err := tx.Exec("UPDATE test_wallet SET balance=? WHERE id=?", w2.Balance+7, wID)
if err != nil {
log.Println(err)
}
n, err := res.RowsAffected()
if n != 1 {
log.Println("update not affected r2")
}
tx.Commit()
log.Println("done on r2")
}()
wg.Wait()
w := &Wallet{}
err = db.Get(w, "SELECT * FROM test_wallet WHERE id=?", wID)
if err != nil {
log.Println(err)
}
log.Printf("%+v\n", w)
}
result from my terminal
2016/10/01 09:57:00 got &{ID:aab7165c-4b3b-406d-b1d0-caf3f45f72be Balance:10} on r1
2016/10/01 09:57:01 got &{ID:aab7165c-4b3b-406d-b1d0-caf3f45f72be Balance:10} on r2
2016/10/01 09:57:01 done on r2
2016/10/01 09:57:03 done on r1
2016/10/01 09:57:03 &{ID:aab7165c-4b3b-406d-b1d0-caf3f45f72be Balance:15}
it seem the second routine has not been lock ???
You misused the transaction. The db is not in transaction, only tx is. So, the statements in first and second go routine which are
err = db.Get(w1, "SELECT * FROM test_wallet WHERE id=? FOR UPDATE", wID) // read and lock the record
err = db.Get(w2, "SELECT * FROM test_wallet WHERE id=? FOR UPDATE", wID)
will not lock the row, because db is not wrapped in transaction. You must use tx to execute the query, i.e.
err = tx.Get(w1, "SELECT * FROM test_wallet WHERE id=? FOR UPDATE", wID) // read and lock the record
err = tx.Get(w2, "SELECT * FROM test_wallet WHERE id=? FOR UPDATE", wID)
After modification, I got the following result:
2016/10/01 13:26:10 got &{ID:6ff45acd-701c-458f-a17f-84cc4e982c80 Balance:10} on r1
2016/10/01 13:26:14 done on r1
2016/10/01 13:26:14 got &{ID:6ff45acd-701c-458f-a17f-84cc4e982c80 Balance:15} on r2
2016/10/01 13:26:14 done on r2
2016/10/01 13:26:14 &{ID:6ff45acd-701c-458f-a17f-84cc4e982c80 Balance:22}