golang mysql driver failing at parameter replacement - mysql

OS Mojave,
MySQL v8.0.15,
go 1.12.3 darwin/amd64
import (
"database/sql"
// import mysql driver anonymously (just run the init)
_ "github.com/go-sql-driver/mysql"
)
...
_, err = db.db.Exec("USE ?", "test")
if err != nil {
return errors.Wrapf(err, "error selecting database %s", opt.Database)
}
_, err = db.db.Prepare("SELECT value FROM ? WHERE key = ?")
if err != nil {
return errors.Wrap(err, "error generating SELECT statement")
}
The error I get is error selecting database test: Error 1064: You have an error in your SQL syntax; check the manual that corresponds to your MySQL server version for the right syntax to use near '?' at line 1
I get the same error (about syntax around '?') for any parameter replacement I attempt to do. I've checked the manual as well as tutorials and examples I've found online and can't quite tell what I'm doing wrong.
If I replace the '?'s with values (strings) then everything works fine.

Question marks ? in prepared statements are for values, not table, database or column names.
Use fmt.Sprintf to fill in the database structure related values.
Example:
_, err := db.db.Prepare(fmt.Sprintf("SELECT value FROM %s WHERE key = ?", table))

Related

Can't retrieve output parameter with Go MySQL

I have a stored procedure which inserts an entity and returns its new UUID, and while I can generate the code to create the right query, it errors out when using go-sql-driver/mysql. So the following code...
func test_insert() *sql.Rows {
db := openDbConnection()
defer db.Close()
results, err := db.Query("call saveargument(null, 'Test Argument', 'Test Argument', '1', null, null, null, 1, 'test_user', #newargumentid);\nselect #newargumentid;")
toolkit.HandleError(err)
return results
}
func openDbConnection() *sql.DB {
var db, err = sql.Open("mysql", getConnectionString(entities.GetConfig()))
toolkit.HandleError(err)
return db
}
... produces the following error:
You have an error in your SQL syntax; check the manual that
corresponds to your MySQL server version for the right syntax to use
near 'select #newargumentid' at line 2
I'm not sure why such a basic piece of SQL could be so problematic. Any insights anyone?
You can't run more than one statement in a single db.Query() call. This is true of most query interfaces in all programming languages.
Call your stored procedure in one call to db.Query(), then query select #newargumentid in a second call to db.Query().
I just tested #Bill's answer and I can confirm it does not work as at Go1.14. My workaround was to rewrite my stored procedure to "return" values by doing a select at the end:
CREATE PROCEDURE foo()
BEGIN
# do stuff...
SELECT 'bar';
END;
And then in Go just read it like any other query:
res, _ := db.Query("CALL foo()")
res.Next()
var bar string
res.Scan(&bar)
println(bar)
Note this method also works for multiple columns and multiple rows.

Using placeholder ? in Go mySql query for anything other than int

I've already setup and pinged my mysql database connection. It is working and I can return rows using both db.Query and by preparing a query first. I can use the placeholder ? to then specify an id. Is it possible to use the ? as a placeholder for a column name? In the example here I am trying to return all rows from column firstName in table persons.
qry, err := db.Prepare("SELECT ? FROM persons")
if err != nil { log.Fatal(err) }
defer qry.Close()
rows, err :=qry.Query("firstName")
if err != nil { log.Fatal(err) }
defer rows.Close()
I get the following error:
Error 1064: You have an error in your SQL syntax;
check the manual that corresponds to your MySQL server version for the right
syntax to use near '?' at line 1
You can't use placeholders for identifiers (such as table and column names), placeholders are for values. You can think of identifiers as being similar to variable or function names in Go so being able to use placeholders for identifiers would be akin to having an eval as in various scripting languages.
This reduces you to using fmt.Sprintf and similar string operations for building the SQL when you don't know the identifiers until runtime:
col := "firstName"
sql := fmt.Sprintf("select %s from persons", col)
but this opens you up to SQL injection and quoting problems so you'd want some sort of whitelist:
quotedColumns := map[string]string{
"firstName": "`firstName`",
"lastName": "`lastName`",
...
}
quoted, ok := quotedColumns[columnName]
if !ok {
// Do something with the error here and run away...
}
sql := fmt.Sprintf("select %s from persons", quoted)
Note that I've included the MySQL backtick quoting in the map's values. There's nothing in the standard interface for quoting/escaping an identifier so you have to do it yourself. If you're already writing the whitelist map by hand then you may as well include the quoting by hand too; otherwise you could write your own quoting function for identifiers by reading the MySQL documentation on quoting and doing a couple (hopefully) simple string operations.

mysql complains at syntax from go driver

I'm using the github.com/go-sql-driver/mysql and mysql 5.7.10. I have a function:
bulkSetStatus := func(docVers []*_documentVersion) error {
if len(docVers) > 0 {
query := strings.Repeat("CALL documentVersionSetStatus(?, ?); ", len(docVers))
args := make([]interface{}, 0, len(docVers)*2)
for _, docVer := range docVers {
args = append(args, docVer.Id, docVer.Status)
}
_, err := db.Exec(query, args...)
return err
}
return nil
}
which works if len(docVers) == 1 but when there are more, resulting in multiple CALLs to the stored procedure, it errors:
Error 1064: You have an error in your SQL syntax; check the manual that corresponds to your MySQL server version for the right syntax to use near 'CALL documentVersionSetStatus(?, ?)' at line 1
I have also tried a newline character between each call but I get the same error. If I run this in mysql workbench with multiple CALLs to this procedure it works fine, I'm not sure what is wrong with the syntax here.
I have logged out the exact full text with the arguments and it is as expected:
CALL documentVersionSetStatus("9c71cac14a134e7abbc4725997d90d2b", "inprogress"); CALL documentVersionSetStatus("beb65318da96406fa92990426a279efa", "inprogress");
go-sql-driver, by default, does not allow you to have multiple statements in one query (as you are doing by chaining together multiple CALL statements like that) due to the security implications if an attacker manages to perform SQL injection (for example, by injecting 0 OR 0; DROP TABLE foo).
To allow this, you must explicitly enable it by passing multiStatements parameter when connecting to the database, e.g.
db, err := sql.Open("mysql", "user:password#/dbname?multiStatements=True")
Source: https://github.com/go-sql-driver/mysql#multistatements
I have fixed the proc call by doing some manual string interpolation for the parameters instead of using the correct ? way of doing it:
bulkSetStatus := func(docVers []*_documentVersion) error {
if len(docVers) > 0 {
query := strings.Repeat("CALL documentVersionSetStatus(%q, %q); ", len(docVers))
args := make([]interface{}, 0, len(docVers)*2)
for _, docVer := range docVers {
args = append(args, docVer.Id, docVer.Status)
}
_, err := db.Exec(fmt.Sprintf(query, args...))
return err
}
return nil
}
so I swap out the ? for %q and us fmt.Sprintf to inject the parameters, I should note that slugonamission's answer is partially correct, I did need to add the connection string parameter multiStatements=true in order to get this to work with my other changes. I will log an issue on the github repo it looks like there may be some param interpolation issue when there is more than one statement, I think the error was happening because the mysql db was trying to run the script with ? literals in it.

Go - Connecting to a external mysql database

I'm rather new to use go and am having issues connecting to an external mysql database.
I'm using the go-sql-driver which seams rather nice. Suggestions to other drivers are welcomed!
this is the whole program:
import (
"database/sql"
_ "github.com/go-sql-driver/mysql"
"fmt"
)
const (
DB_HOST = "tcp(http://thedburl.com)"
DB_NAME = "nameofdatabase"
DB_USER = "username"
DB_PW = "password"
)
func main() {
dsn := DB_USER + ":" + DB_PW + "#" + DB_HOST + "/" + DB_NAME + "?charset=uf8"
db, err := sql.Open("mysql", dsn)
if err != nil {
fmt.Println("shiiet didn't work yo! Initialization failed")
}
defer db.Close() // go's purty cool
var str string
q := "SELECT * FROM forums"
err = db.QueryRow(q).Scan(&str)
if err != nil {
fmt.Println(err)
}
fmt.Println(str)
}
On the request I'm recieving the following error
"GetAddrInfoW: The specified class was not found."
Any ideas? I've siting for hours on the webs, and can't seem to solve the problem.
It might be worth noting that I have used the same database service many times in java.
Interestingly, this error can be also caused by spaces in the server port as in server.Run(":8080 ") vs server.Run(":8080")
Thanks for everyones answers.
The mysql drivers for Go are at the current time having a hard time dealing with older mysql versions. Specifically this issue happened due to the incompatibility of mysql's old_password from 2006. Therefore working with older databases is a pain. (source: https://github.com/go-sql-driver/mysql/wiki/old_passwords) - My educated guess also applies to the mymysql driver whom throws the "bad connection" error. Which basicly times out after repetitive attempts of connection.
Special thanks to #Lepidosteus who's answer made me discover the real problem.

go convert empty interface to string

I'm using the mymysql package and I'm trying to create a function which gets an SQL query and some parameters (as variadic empty interface):
func FindByQuery(statement string, params ...interface{}) (diver *DiverT, err error) {
values := make([]interface{}, len(params))
for i := range params {
values[i] = params[i]
}
// Both statements result in the same error...
row, _, execError := Db.QueryFirst(statement,values...)
row, _, execError := Db.QueryFirst(statement,params...)
// Additional code...
}
When I call this method using some kind of SQL, I always get an SQL error. I do something like:
FindByQuery("SELECT * FROM Diver WHERE Name=?", "Markus")
Which results in the following error:
Received #1064 error from MySQL server: "You have an error in your SQL syntax; check the manual that corresponds to your MySQL server version for the right syntax to use near '?%!(EXTRA string=Markus)' at line 1"
What should I do that the parameter is converted correctly to a string (or whatever it is, if I have different parameter(s))?
Try using printf format syntax:
FindByQuery("SELECT * FROM Diver WHERE Name=%s", "Markus")