golang mysql do not replace placeholder - mysql

I am using sqlx for with mysql driver, but when I execute an insert statement I got "Error 1064: You have an error in your SQL syntax;"
import (
_ "github.com/go-sql-driver/mysql"
"github.com/jmoiron/sqlx"
)
func main(){
db, _ := sqlx.Connect("mysql", "connetionStr")
query := fmt.Sprintf("insert into bot(poster_id,name,welcome_msg,exit_msg) values(?,?,\"%s\",\"%s\")", "hello", "bye")
result := db.MustExec(query, 1, "bot_name")
}
The syntax is certainly not wrong there. The problem is with the db.MustExec function. Why is it not replacing the ? place holder

Try this:
query := fmt.Sprintf("insert into bot(poster_id,name,welcome_msg,exit_msg) values(?,?,'%s','%s')", "hello", "bye")
result := db.MustExec(query, 1, "bot_name")

Related

Strange number when using fmt.Println in Golang

I'm new to Golang and have been doing alright but I have a strange issue that I have not encountered before when using fmt. This strange behavior is when I'm printing a string. At the end of the string (which has sub-strings) it is also printing out what appears to be the len() of each string although the number don't add up. Can anyone explain why this is happening and how to stop it?
Any help is greatly appreciated
Here is the code:
package main
import (
"fmt"
//"log"
"strings"
)
var e = "[{8888a8558921d75ec8bc362efbe9a76b82ec002337534e9f06ce92cbf8c27c8888 localhost:3303 4d50f447-7c93-42df-a03e-89c09626950a}]"
func main() {
tl := strings.Trim(e, "[{")
tr := strings.Trim(tl, "}]")
r := strings.TrimSpace(tr)
s := strings.Fields(r)
V_PK := s[0]
SERVER_ADDR := s[1]
A_KEY := s[2]
vv, _ := fmt.Printf("[{\"v_pk\": %q", V_PK)
pp, _ := fmt.Printf(",\"server_addr\": %q", SERVER_ADDR)
kk, _ := fmt.Printf(",\"a_key\": %q}] ", A_KEY)
rstr, _ := fmt.Println(vv, pp, kk)
stringc := string(rstr)
fmt.Println(stringc)
}
Expected output:
[{"v_pk": "8888a8558921d75ec8bc362efbe9a76b82ec002337534e9f06ce92cbf8c27c8888","server_addr": "localhost:3303","a_key": "4d50f447-7c93-42df-a03e-89c09626950a"}]
Actual output:
[{"v_pk": "8888a8558921d75ec8bc362efbe9a76b82ec002337534e9f06ce92cbf8c27c8888","server_addr": "localhost:3303","a_key": "4d50f447-7c93-42df-a03e-89c09626950a"}] 82 36 53
Why on earth would it be printing these string lengths on the end? It's probably obvious that I'm trying to build a JSON string so these numbers on the end are problematic when trying to import the string into a JSON interpreter.
Again, any help is appreciated!
Take a look at the documentation for fmt.Printf and its friends fmt.Println. The documentation reads:
Printf formats according to a format specifier and writes to standard output. It returns the number of bytes written and any write error encountered.
The line in your code
vv, _ := fmt.Printf("[{\"v_pk\": %q", V_PK)
prints the formatted string to standard output, then return the number of bytes written and stores that in vv. If you want to print the formatted string to standard output, just call fmt.Printf and ignore the output:
package main
import (
"fmt"
//"log"
"strings"
)
var e = "[{8888a8558921d75ec8bc362efbe9a76b82ec002337534e9f06ce92cbf8c27c8888 localhost:3303 4d50f447-7c93-42df-a03e-89c09626950a}]"
func main() {
tl := strings.Trim(e, "[{")
tr := strings.Trim(tl, "}]")
r := strings.TrimSpace(tr)
s := strings.Fields(r)
V_PK := s[0]
SERVER_ADDR := s[1]
A_KEY := s[2]
fmt.Printf("[{\"v_pk\": %q, \"server_addr\": %q, \"a_key\": %q}]\n", V_PK, SERVER_ADDR, A_KEY)
}
Or, if you want to store the formatted string to a new string variable, call fmt.Sprintf:
stringc := fmt.Sprintf("[{\"v_pk\": %q, \"server_addr\": %q, \"a_key\": %q}]", V_PK, SERVER_ADDR, A_KEY)
fmt.Println(stringc)
You can check out a working version at the playground.
You might also want to checkout the json package, which can do the parsing and serializing for you with properly defined structs:
package main
import (
"encoding/json"
"fmt"
)
func main() {
type Datum struct {
VPK string `json:"v_pk"`
Server string `json:"server_addr"`
AKey string `json:"a_key"`
}
data := []Datum{
{VPK: "8888a8558921d75ec8bc362efbe9a76b82ec002337534e9f06ce92cbf8c27c8888",
Server: "localhost:3303",
AKey: "4d50f447-7c93-42df-a03e-89c09626950a",
}}
json, err := json.MarshalIndent(data, "", " ")
if err != nil {
// deal with error
}
fmt.Println(string(json))
}
Check it out at the go playground.
fmt.Printf returns the number of bytes written. The variables vv, pp, kk are the number of bytes written by those three Printf calls, and the three numbers printed are those numbers.

How to test mysql insert method

I'm setting up testing in Go. I use go-sqlmock to test mysql connection. Now I try to test mysql insert logic. But the error occurs.
I want to know how to resolve this error.
server side: golang
db: mysql
web framework: gin
dao.go
func PostDao(db *sql.DB, article util.Article, uu string) {
ins, err := db.Prepare("INSERT INTO articles(uuid, title,content) VALUES(?,?,?)")
if err != nil {
log.Fatal(err)
}
ins.Exec(uu, article.TITLE, article.CONTENT)
}
dao_test.go
func TestPostArticleDao(t *testing.T) {
db, mock, err := sqlmock.New()
if err != nil {
t.Fatalf("an error '%s' was not expected when opening a stub database connection", err)
}
defer db.Close()
mock.ExpectExec("^INSERT INTO articles*").
WithArgs("bea1b24d-0627-4ea0-aa2b-8af4c6c2a41c", "test", "test").
WillReturnResult(sqlmock.NewResult(1, 1))
article := util.Article{
ID: 1,
TITLE: "test",
CONTENT: "test",
}
PostDao(db, article, "bea1b24d-0627-4ea0-aa2b-8af4c6c2a41c")
if err := mock.ExpectationsWereMet(); err != nil {
t.Errorf("there were unfulfilled expections: %s", err)
}
}
I expect go test -v runs without error.
But the actual is not.
Here is the error.
=== RUN TestPostArticleDao
2019/08/31 00:08:11 call to Prepare statement with query 'INSERT INTO articles(uuid, title,content) VALUES(?,?,?)', was not expected, next expectation is: ExpectedExec => expecting Exec or ExecContext which:
- matches sql: 'INSERT INTO articles(uuid, title,content) VALUES(?,?,?)'
- is with arguments:
0 - bea1b24d-0627-4ea0-aa2b-8af4c6c2a41c
1 - test
2 - test
- should return Result having:
LastInsertId: 1
RowsAffected: 1
exit status 1
FAIL article/api/dao 0.022s
As #Flimzy suggested, it needs to set ExpectPrepare first.
So I changed dao_test.go in this way:
prep := mock.ExpectPrepare("^INSERT INTO articles*")
prep.ExpectExec().
WithArgs("bea1b24d-0627-4ea0-aa2b-8af4c6c2a41c", "test", "test").
WillReturnResult(sqlmock.NewResult(1, 1))
In my case it worked without asterix:
mock.ExpectExec("INSERT INTO `mytable`").WithArgs(mockdbutils.AnyTime{}, mockdbutils.AnyTime{}, nil, 4455,false).WillReturnResult(sqlmock.NewResult(int64(4455), 1))
mock.ExpectCommit()

unexpected EOF and busy buffer in (go-sql-driver/mysql) packets.go

I am getting the unexpected EOF and busy buffer error in go-sql-driver/mysql despite after setting the SetConnMaxLifetime, SetMaxIdleConns and SetMaxOpenConns as suggested here. Can anyone tell me the proper solution of this issue nothing seems to work for me?
db, err := sql.Open("mysql", "USERNAME:PASSWORD#tcp(IP:PORT)/DB?charset=utf8")
checkErr(err)
db.SetConnMaxLifetime(time.Second * 5)
db.SetMaxIdleConns(0)
db.SetMaxOpenConns(151)
rows, err := db.Query("Select col1, col2, col3 from tbl")
checkErr(err)
for rows.Next() {
var col1 string
var col2 int32
var col3 uint64
err = rows.Scan(&col1, &col2, &col3)
checkErr(err)
Process(col1, col2, col3)
}
I setup a local MySQL database and ran your code:
package main
import (
"database/sql"
"fmt"
"log"
"time"
_ "github.com/go-sql-driver/mysql"
)
func main() {
db, err := sql.Open("mysql", "root#tcp(localhost)/test?charset=utf8")
if err != nil {
log.Fatalln(err)
}
db.SetConnMaxLifetime(time.Second * 5)
db.SetMaxIdleConns(0)
db.SetMaxOpenConns(151)
rows, err := db.Query("SELECT col1, col2, col3 FROM tbl2")
if err != nil {
log.Fatalln(err)
}
for rows.Next() {
var col1 string
var col2 int32
var col3 uint64
err = rows.Scan(&col1, &col2, &col3)
if err != nil {
log.Fatalln(err)
}
fmt.Println(col1, col2, col3)
}
}
..and it worked just fine for me. My CREATE TABLE statement looks like this:
CREATE TABLE `tbl2` (
`col1` varchar(25) DEFAULT NULL,
`col2` int(11) DEFAULT NULL,
`col3` bigint(20) unsigned DEFAULT NULL
) ENGINE=InnoDB DEFAULT CHARSET=utf8;
What does your table structure look like?
I found two ways for work arround the packets.go:36: unexpected EOF error, first way was changing driver to ziutek/mymysql driver, works ok and more or less with the same performance.
Second way, with the default driver, is to set db.SetMaxIdleConns(100) and db.SetMaxOpenConns(100), my mysql has max_connections to 151,
so I thought that limit to 100 will be ok.
In addition, prepared statements has speeded up a lot, before they were slower than Db.Query, now are at least twice as fast. (Tested with 200k queries in 20 goroutines)
I think the problem could be in file buffer.go of driver go-sql-driver/mysql, in line: nn, err := b.nc.Read(b.buf[n:]), some kind of timeout.
None of these worked for me. I have used the Docker container then I tried to log in to it from the local machine with MySQL client.
mysql -h localhost -P 3306 --protocol=tcp
After this go programme worked fine.
According to go-sql-driver/mysql/issues/314, bad_buffer could also happen when attempting to run multiple statements within a transaction while still having an open *sql.Rows.
Ensuring that all *sql.Rows were closed before running subsequent statements resolves the issue.
Before
rows, queryErr = tx.Query(selectQuery, queryArgs...)
// process rows.Next()
// Attempt to perform additional query
rows2, query2Err = tx.Query(selectQuery2, query2Args ...)
// Get bad_buffer error (from logs:)
// [mysql] {timestamp} packets.go:447: busy buffer
// [mysql] {timestamp} connection.go:173: bad connection
After
rows, queryErr = tx.Query(selectQuery, queryArgs...)
defer func(result *sql.Rows) {
_ = result.Close()
}(rows)
// process rows.Next()
// Attempt to perform additional query
rows2, query2Err = tx.Query(selectQuery2, query2Args ...)
defer func(result *sql.Rows) {
_ = result.Close()
}(rows2)
// No error

How to execute Mysql Script in golang using exec.Command

Hi i am trying to execute a script to fill data into a database using Golang
func executeTestScript(){
cmd := exec.Command("/usr/local/mysql/bin/mysql", "-h127.0.0.1", "-P3333", "-uusr", "-pPassxxx", "-Ddtb_test", "< /Users/XXX/Documents/test/scripts/olds/SCRIPT_XXX.sql")
var out, stderr bytes.Buffer
cmd.Stdout = &out
cmd.Stderr = &stderr
err := cmd.Run()
if err != nil {
fmt.Println(fmt.Sprintf("Error executing query. Command Output: %+v\n: %+v, %v", out.String(), stderr.String(), err))
log.Fatalf("Error executing query. Command Output: %+v\n: %+v, %v", out.String(), stderr.String(), err)
}
}
The problem is that i am getting the error:
ERROR 1049 (42000): Unknown database '< /Users/XXX/Documents/test/scripts/olds/SCRIPT_XXX.sql'
i think the problem is the last param (the sql script path) that the exec thinks is the dbname
The following command in the terminal is working:
/usr/local/mysql/bin/mysql --host=127.0.0.1 --port=3333 --user=usr --password=Passxxx --database=dtb_test < /Users/XXX/Documents/roseula/scripts/olds/SCRIPT_XXX.sql
but i try to replicate in Go to automatize the execution of the script.
The script have drop tables, create tables, inserts, and PK with FK relationships its a very complete one so i cant execute line by line, because of that i decided to execute de mysql program to insert the data in the database.
Any suggestions?
+1 to answer from #MatteoRagni for showing how to do stdin redirection in Golang.
But here's a simple alternative that I use:
cmd := exec.Command("/usr/local/mysql/bin/mysql", "-h127.0.0.1", "-P3333",
"-uusr", "-pPassxxx", "-Ddtb_test",
"-e", "source /Users/XXX/Documents/test/scripts/olds/SCRIPT_XXX.sql")
You don't have to make the mysql client read the script using stdin redirection. Instead, you can make the mysql client execute a specific command, which is source <scriptname>.
P.S.: I also would not put the host, port, user, and password in your code. That means you have to recompile your program any time you change those connection parameters. Also it's not secure to use passwords in plaintext on the command-line. Instead, I'd put all the connection parameters into a defaults file and use mysql --defaults-file=FILENAME.
This is a little example that runs something like:
cat < test.txt
that is what I think you are missing in your code:
package main
import (
"fmt"
"os/exec"
"os"
)
func main() {
cmd := exec.Command("cat")
file, _ := os.Open("test.txt")
cmd.Stdin = file
out, _ := cmd.Output()
fmt.Printf("%s\n", out)
}
That prints in the console the content of test.txt, as read by cat. You will need to adapt it to your problem.
Something like:
func executeTestScript(){
cmd := exec.Command("/usr/local/mysql/bin/mysql", "-h127.0.0.1", "-P3333", "-uusr", "-pPassxxx", "-Ddtb_test")
dump, dump_err = os.Open("/Users/XXX/Documents/test/scripts/olds/SCRIPT_XXX.sql")
if dump_err != nil {
/* Handle the error if file not opened */
}
cmd.Stdin = dump
var out, stderr bytes.Buffer
cmd.Stdout = &out
cmd.Stderr = &stderr
err := cmd.Run()
if err != nil {
fmt.Println(fmt.Sprintf("Error executing query. Command Output: %+v\n: %+v, %v", out.String(), stderr.String(), err))
log.Fatalf("Error executing query. Command Output: %+v\n: %+v, %v", out.String(), stderr.String(), err)
}
}
if I'm not wrong...

Golang MySQL Database Not Selected

I'm using github.com/go-sql-driver/mysql package to connect to MySQL. It works well except when I select a database (USE), I can't run queries against it.
package main
import (
"database/sql"
"fmt"
"log"
)
import _ "github.com/go-sql-driver/mysql"
func main() {
dsn := "root:#/"
db, err := sql.Open("mysql", dsn)
if err != nil {
fmt.Println("Failed to prepare connection to database. DSN:", dsn)
log.Fatal("Error:", err.Error())
}
err = db.Ping()
if err != nil {
fmt.Println("Failed to establish connection to database. DSN:", dsn)
log.Fatal("Error:", err.Error())
}
_, err = db.Query("USE test")
if err != nil {
fmt.Println("Failed to change database.")
log.Fatal("Error:", err.Error())
}
_, err = db.Query("SHOW TABLES")
if err != nil {
fmt.Println("Failed to execute query.")
log.Fatal("Error:", err.Error())
}
}
The program produces this output:
Error 1046: No database selected
Specify the database directly in the DSN (Data Source Name) part of the sql.Open function:
dsn := "user:password#/dbname"
db, err := sql.Open("mysql", dsn)
That's because db maintains a connection pool that has several connections to mysql database."USE test" just let one connection use schema test.
When you do database query later,the driver will select one idle connection,if the connection that use test schema is selected,it will be normal,but if another connection is chosen, it does not use test,so it will report an error:no database selected.
If you add a clause:
db.SetMaxOpenConns(1)
the db will maintain only one connection,it will not have an error.And of course it's impossible in high concurrency scene.
If you specify the database name in sql.open() function,all the connection will use this data base which can avoid this problem.
In your case you need to use transactions:
tx, _ := db.Begin()
tx.Query("USE test")
tx.Query("SHOW TABLES")
tx.Commit()
For SELECT/UPDATE/INSERT/etc need to specify DB name in the query.
As other answers mentioned, sql.DB is not a single connection but a connection pool. When you execute use database, imagine you executed your query on just one connection in the pool. Next query will get another connection from the pool which has no databases selected.
I would strongly advise against using transactions for this (as several places suggest).
I would suggest to use context:
ctx := context.Background()
conn, err := db.Conn(ctx)
conn.ExecContext(ctx, "use mydb")
defer conn.Close()
var found int
err = conn.QueryRowContext(ctx, "SELECT count(*) as found FROM mytable").Scan(&found)
if err != nil {
panic(err)
}