Calling Stored Procedure in GoLang with QueryRowContent - mysql

Im trying to run the below code segment and keeps throwing the below error from QueryRowContext
sql: expected 0 arguments, got 2
This approach works with plain SQL queries and I keep getting the issue when I try to call a Stored Prod to the query param with the CALL keyword.
import (
"database/sql"
"net/http"
)
func VerifyUser(user User) (*User, string, error) {
db, ctx := db.GetDB()
query := "CALL usp_GetUserByUsername(#Email)"
stmt, err := db.Prepare(query)
if err != nil {
log.Errorln("Error in preparing statement. " + err.Error())
return nil, "Error in preparing statement.", err
}
defer stmt.Close()
row := stmt.QueryRowContext(ctx, sql.Named("Email", user.Email))
var retUser User
err = row.Scan(&retUser.ID, &retUser.Email, &retUser.Password, &retUser.Status)
if err != nil {
log.Warningln("Unknown Email: " + user.Email + ". " + err.Error())
return nil, "Invalid user.", err
}
What seems to be wrong here? Thanks in advance.

import (
"database/sql"
"net/http"
)
func VerifyUser(user User) (*User, string, error) {
db, ctx := db.GetDB()
query := "CALL usp_GetUserByUsername(?)"
stmt, err := db.Prepare(query)
if err != nil {
log.Errorln("Error in preparing statement. " + err.Error())
return nil, "Error in preparing statement.", err
}
defer stmt.Close()
row := stmt.QueryRowContext(ctx, user.Email)
var retUser User
err = row.Scan(&retUser.ID, &retUser.Email, &retUser.Password, &retUser.Status)
if err != nil {
log.Warningln("Unknown Email: " + user.Email + ". " + err.Error())
return nil, "Invalid user.", err
}
Replace #Email in your query with ? and pass the email into QueryRowContext not named statement

Related

I could not connect Mysql with ODBC in Golang

I want to connect mssql, mysql, postgres database servers with ODBC in Go. I have used this package: https://github.com/alexbrainman/odbc
I could connect my local mssql but I could not connect mysql. It gives this error: "Query Preparation Error SQLDriverConnect: {IM002} [Microsoft][ODBC Driver Manager] Data source name not found and no default driver specified"
My OS is Windows 10. After that I will also try on Ubuntu.
This is my code:
import (
"database/sql"
"fmt"
"log"
_ "database/sql/driver"
_ "github.com/alexbrainman/odbc"
)
func main() {
// conn, err := sql.Open("odbc",
// "uid=sa;pwd=Str0ngPa$$w0rd;port=1433;driver=sql server;server=host.docker.internal;database=master;TDS_Version=7.2;")
dbConn := fmt.Sprintf("driver=mysql;server=%s;port=3306;=database=%s;user=%s;password=%s;TDS_Version=7.2;",
"host.docker.internal", "sys", "root", "secret")
conn, err := sql.Open("odbc", dbConn)
if err != nil {
fmt.Println("Connecting Error")
return
}
defer conn.Close()
fmt.Println("Sql Server Connected")
stmt, err := conn.Prepare("select thirdtest, onetest from firsttable WHERE secondtest >= 5 limit 5")
if err != nil {
fmt.Println("Query Preparation Error", err)
return
}
defer stmt.Close()
fmt.Println("Query Prepared")
// Use db.Query() to send the query to the database. Check errors as usual.
row, err := stmt.Query()
if err != nil {
fmt.Println("Query Error", err)
return
}
defer row.Close()
fmt.Printf("\nResult set 1:\n")
for row.Next() {
var (
thirdtest float64
onetest string
)
fmt.Println("result 1")
err := row.Scan(&onetest, &thirdtest)
if err == nil {
fmt.Println(onetest, thirdtest)
}
}
err = row.Err()
if err != nil {
fmt.Printf("\nFatal: %s\n", err)
}
stmt, err = conn.Prepare("select top 5 database_id, name from sys.databases WHERE database_id >= ?")
if err != nil {
log.Fatal(err)
}
row, err = stmt.Query(1)
if err != nil {
log.Fatal(err)
}
defer row.Close()
fmt.Printf("\nResult set 2:\n")
for row.Next() {
var (
id int
name string
)
if err := row.Scan(&id, &name); err == nil {
fmt.Println(id, name)
} else {
log.Fatal(err)
}
}
err = row.Err()
if err != nil {
log.Fatal(err)
}
fmt.Printf("\nFinished correctly\n")
return
}

GoLang Dynamic SQL Query in App Engine

I want to make dynamic sql in GoLang and I cant seem to find the correct way to do it.
Basically, I just want to do:
query := "SELECT id, email, something FROM User"
var paramValues []string
filterString := ""
if userParams.Name != "" {
paramString += " WHERE id = ?"
paramValues = append(paramValues, userParams.Name)
}
if userParams.UserID != "" {
if len(paramString) > 0 {
paramString += " AND"
} else {
paramString += " WHERE"
}
paramString += " email = ?"
paramValues = append(paramValues, userParams.UserID)
}
stmtOut, err := db.Prepare(query + paramString)
err = stmtOut.QueryRow(paramValues).Scan(&id, &email, &something)
Related to building a dynamic query in mysql and golang
I've been unable to find a solid way to do this that doesn't allow sql injection. The issue with my above solution is that QueryRow() does not take a []string as a parameter.
I want to protect from SQL Injection, so fmt.Sprintf doesn't really solve the problem.
This way I can allow searches on user using either the ID or Email, and I will also use this logic for different objects with more searchable fields.
I'm using go-sql-driver/mysql
Here's something which I can run on my local machine (go1.8 linux/amd64 and current GO MySQL driver 1.3).
Couple of ways are demonstrated.
package main
import (
"database/sql"
"log"
_ "github.com/go-sql-driver/mysql"
"fmt"
)
// var db *sql.DB
// var err error
/*
Database Name/Schema : Test123
Table Name: test
Table Columns and types:
number INT (PRIMARY KEY)
cube INT
*/
func main() {
//Username root, password root
db, err := sql.Open("mysql", "root:root#tcp(127.0.0.1:3306)/Test123?charset=utf8")
if err != nil {
fmt.Println(err) // needs proper handling as per app requirement
return
}
defer db.Close()
err = db.Ping()
if err != nil {
fmt.Println(err) // needs proper handling as per app requirement
return
}
//Prepared statement for inserting data
stmtIns, err := db.Prepare("INSERT INTO test VALUES( ?, ? )") // ? = placeholders
if err != nil {
panic(err.Error()) // needs proper handling as per app requirement
}
defer stmtIns.Close()
//Insert cubes of 1- 10 numbers
for i := 1; i < 10; i++ {
_, err = stmtIns.Exec(i, (i * i * i)) // Insert tuples (i, i^3)
if err != nil {
panic(err.Error()) // proper error handling instead of panic in your app
}
}
num := 3
// Select statement
dataEntity := "cube"
condition := "WHERE number=? AND cube > ?"
finalStatement := "SELECT " + dataEntity + " FROM test " + condition
cubeLowerLimit := 10
var myCube int
err = db.QueryRow(finalStatement, num, cubeLowerLimit).Scan(&myCube)
switch {
case err == sql.ErrNoRows:
log.Printf("No row with this number %d", num)
case err != nil:
log.Fatal(err)
default:
fmt.Printf("Cube for %d is %d\n", num, myCube)
}
var cubenum int
// //Prepared statement for reading data
stmtRead, err := db.Prepare(finalStatement)
if err != nil {
panic(err.Error()) // needs proper err handling
}
defer stmtRead.Close()
// Query for cube of 5
num = 5
err = stmtRead.QueryRow(num, cubeLowerLimit).Scan(&cubenum)
switch {
case err == sql.ErrNoRows:
log.Printf("No row with this number %d", num)
case err != nil:
log.Fatal(err)
default:
fmt.Printf("Cube number for %d is %d\n", num, cubenum)
}
}
If you run it subsequent times, you need to delete the rows in the database so that the inserts won't create a panic (or alternatively change the insert rows code so that it doesn't panic). I haven't tried it on Google App Engine. Hope this helps.

Else condition seems not working in go

I have a MySQL database with one value in it, a string: "192.168.0.1"
Here is my code:
package main
import (
"database/sql"
"fmt"
_ "github.com/go-sql-driver/mysql"
)
func checkErr(err error) {
if err != nil {
panic(err)
}
}
func main() {
db, err := sql.Open("mysql", "be:me#tcp(127.0.0.1:3306)/ipdb?charset=utf8")
checkErr(err)
ip := "'192.168.0.1'"
rows, err := db.Query("SELECT * FROM Ip_ipdata WHERE ipHost=" + ip)
fmt.Println("insert")
if rows != nil {
for rows.Next() {
var id int
var ip string
err = rows.Scan(&id, &ip)
checkErr(err)
fmt.Println(id)
fmt.Println(ip)
}
} else {
fmt.Println("insert2")
stmt, err2 := db.Prepare("INSERT Ip_ipdata SET ipHost=2")
checkErr(err2)
_, err3 := stmt.Exec(ip)
checkErr(err3)
}
fmt.Println("end")
}
When I put "'192.168.0.1'" in ip it works and shows as expected.
But when I put "'192.168.0.2'" in ip the else statement isn't run and it just exits.
It didn't print "insert2"
screenshot 1
screenshot 2
You should get used to using '?' placeholders in your sql to allow for proper escaping and prevent any potential SQL injection attacks.
You should always check the error in Go before using the returned value.
ip := "192.168.0.1"
rows, err := db.Query("SELECT * FROM Ip_ipdata WHERE ipHost=?", ip)
if err != nil {
// handle error
}
// this will ensure that the DB connection gets put back into the pool
defer rows.Close()
for rows.Next() {
// scan here
}
The Rows returned by Query will not be nil in the case of no results, it will be empty. Try something like this:
func main() {
...
fmt.Println("insert")
checkErr(err)
defer rows.Close()
var found bool
for rows.Next() {
found = true
...
}
if !found {
fmt.Println("insert2")
...
}
fmt.Println("end")
}
Note that like #jmaloney said, more robust error handling is a must as is closing your Rows pointer.

Neo4j cq driver failed with "Unrecognized transaction id. Transaction may have timed out and been rolled back."

I have recently moved our staging environment from a neo4j 2.2.2 community edition docker container to a 2.3.0-RC1 HA cluster.
This go script provides an API end-point for authenticated users to upload a csv file which then gets processed in a way that it matches the id of a product (upc) with the existing product data. On a match the user's associated distributor creates a relationship to the found product and adds information available in the csv file onto that created relationship.
My problem is that I'm getting a failure from Neo4j in the new environment that I can't track down: "Unrecognized transaction id. Transaction may have timed out and been rolled back." when the user tries to upload the file.
Here's the complete script (my second golang script, so pointers on style are welcome, too).
package main
import (
"database/sql"
"encoding/csv"
"fmt"
_ "gopkg.in/cq.v1"
_ "gopkg.in/cq.v1/types"
"net/http"
"os"
"strconv"
"strings"
)
type ProductInfo struct {
Upc uint64
InStock uint64
BasePrice float64
LeadMin uint64
LeadMax uint64
Sku string
}
func uploadHandler(w http.ResponseWriter, r *http.Request) {
file, _, err := r.FormFile("file")
if err != nil {
fmt.Fprintln(w, err)
return
}
defer file.Close()
distributor := strings.Split(r.URL.Path, "/")[2]
reader := csv.NewReader(file)
reader.FieldsPerRecord = 6
reader.TrimLeadingSpace = true
rawCSVdata, err := reader.ReadAll()
if err != nil {
fmt.Fprintln(w, err)
return
}
db, err := sql.Open("neo4j-cypher", os.Getenv("MINAMI_NEO4J_URL"))
if err != nil {
fmt.Fprintln(w, err)
return
}
defer db.Close()
tx, err := db.Begin()
if err != nil {
fmt.Fprintln(w, err)
return
}
stmt, err := tx.Prepare(`
MATCH (d:Distributor {slug:'` + distributor + `'})
MATCH (p:Product {upc: {0}})
MERGE (d)-[r:SELLS]->(p)
SET r.inStock = {1}, r.sellsFor = {2}, r.leadMin = {3}, r.leadMax = {4}, r.sku = {5}
RETURN p LIMIT 1
`)
if err != nil {
fmt.Fprintln(w, err)
return
}
var Row ProductInfo
for _, each := range rawCSVdata {
Row.Upc, _ = strconv.ParseUint(each[0], 10, 0)
Row.InStock, _ = strconv.ParseUint(each[1], 10, 0)
Row.BasePrice, _ = strconv.ParseFloat(each[2], 0)
Row.LeadMin, _ = strconv.ParseUint(each[3], 10, 0)
Row.LeadMax, _ = strconv.ParseUint(each[4], 10, 0)
Row.Sku = each[5]
stmt.Exec(Row.Upc, Row.InStock, Row.BasePrice, Row.LeadMin, Row.LeadMax, Row.Sku)
}
err = tx.Commit()
if err != nil {
fmt.Fprintln(w, err)
return
}
fmt.Fprintf(w, "File uploaded and products in store updated.")
}
func main() {
http.HandleFunc("/", uploadHandler)
http.ListenAndServe(":8888", nil)
}
I'm using haproxy to know who's master and who are the slaves in the HA cluster. The environment variable MINAMI_NEO4J_URL is set to the master via haproxy in this form: http://{haproxyip}:{masterport}

Can't connect Go to XAMPP MySQL

I'm new to Go.
I'm trying to use MySQL with Go.
I have installed XAMPP which has Apache and MySQL.
I'm also using the database/sql package and github.com/go-sql-driver/mysql driver.
I can't figure out the error. I'm using LiteIDE which doesn't show any error but the record is not inserted to my database.
My code is:
// RESTAPI project main.go
package main
import (
"database/sql"
"encoding/json"
"fmt"
"log"
"net/http"
_ "github.com/go-sql-driver/mysql"
"github.com/gorilla/mux"
)
type API struct {
Message string "json:message"
}
type User struct {
ID int "json:id"
Name string "json:username"
Email string "json:email"
First string "json:first"
Last string "json:last"
}
func CreateUser(w http.ResponseWriter, r *http.Request) {
fmt.Fprintln("Create file")
NewUser := User{}
NewUser.Name = r.FormValue("user")
NewUser.Email = r.FormValue("email")
NewUser.First = r.FormValue("first")
NewUser.Last = r.FormValue("last")
output, err := json.Marshal(NewUser)
fmt.Println(string(output))
if err != nil {
fmt.Println("Something went wrong!")
}
dsn := "root:password#/dbname"
db, err := sql.Open("mysql", dsn)
sql := "INSERT INTO users set user_nickname='" + NewUser.Name + "', user_first='" + NewUser.First + "', user_last='" + NewUser.Last + "', user_email='" + NewUser.Email + "'"
q, err := db.Exec(sql)
if err != nil {
log.Fatal(err)
}
fmt.Println(q)
fmt.Println("User is created")
}
func main() {
routes := mux.NewRouter()
routes.HandleFunc("/api/user/create", CreateUser).Methods("GET")
http.ListenAndServe(":8080", nil)
}
As Tom mentioned, you should check for an error after sql.Open:
db, err := sql.Open("mysql", dsn)
if err != nil {
log.Fatal(err)
}
PS. Never use string concatenations in SQL.
To prevent SQL injections you should use prepared statements:
sql := "INSERT INTO users set user_nickname='?', user_first='?', user_last='?', user_email='?'"
q, err := db.Exec(sql, NewUser.Name, NewUser.First, NewUser.Last, NewUser.Email)