Golang: cannot use sql.NamedArg in prepared statement - mysql

I have a query that is using NamedArgs from the sql.DB and I'm getting an error when building
cannot use args (type []sql.NamedArg) as type []interface {} in argument to stmt.Exec
The example in the SQL library shows it being used:
Example usage:
db.ExecContext(ctx, `
delete from Invoice
where
TimeCreated < #end
and TimeCreated >= #start;`,
sql.Named("start", startTime),
sql.Named("end", endTime),
)
The only difference is that I'm currently using a prepared statement stmt and calling the Exec method on that. I've created a slice of NamedArg with my values and it's using the ... expander.
res, err := stmt.Exec(args...)
What exactly is wrong when the example shows the sql.Named() method call directly in the code? Why wouldn't an expanded slice work?

That's just how passing arguments to a variadic function works in Go. You either pass the individual values which can be of any type, or you pass in a slice whose element type matches exactly that of the variadic parameter and follow it with ....
I.e. you can either do:
res, err := stmt.Exec(
sql.Named("start", startTime),
sql.Named("end", endTime),
)
or you can do:
args := []interface{}{
sql.Named("start", startTime),
sql.Named("end", endTime),
}
res, err := stmt.Exec(args...)

Related

How to extract raw query from dbr golang query builder

I'm new to the golang dbr library (https://godoc.org/github.com/gocraft/dbr)
and I did not find an information about how to get a raw query using this library.
I need something similar to get_compiled_select() from php igniter. I need it to combine multiple complex queries with union.
The following will dump the query...
stmt := session.Select("*").From(table).Where("id = ?", ...)
buf := dbr.NewBuffer()
_ = stmt.Build(stmt.Dialect, buf)
fmt.Println(buf.String())
// print the interpolated values
for _, v := range stmt.WhereCond {
fmt.Println(v)
}
Note that the output will not include the interpolated values.
I'm not so sure previous answer (setting the struct as public) is the wise solution, even if that's works.
IMO, better solution would be creating new getter function inside select.go
func (sel *SelectStmt) GetRaw() string {
return sel.raw.Query
}
With this method, it should be easier to maintain.
u can set raw struct from expr as public.
I hope it helps u.

Get an OUT BLOB type Parameter of a Stored Procedure as TStream

I have a stored procedure on my MySQL Database with 2 OUT parameters as Blob type
I want to get their values as TStream with UniDAC`s SP component, I have tried this code for testing :
SP := TUniStoredProc.Create(nil);
M := TMemoryStream.Create;
try
try
SP.StoredProcName := 'user_getpic';
SP.PrepareSQL(False);
SP.Params.ParamByName('fUID').AsString := '...';
SP.Params.ParamByName('fDiceCode').AsString := '...';
...
SP.ExecProc;
M.LoadFromStream(SP.Params.ParamByName('fUPic').AsStream);
except
on E:EXception do
begin
ShowMessage('Error : ' + E.Message);
end;
end;
finally
SP.Free;
M.Free;
end;
The problem is Param.AsStream returns Nil but Param.AsBlob is not Nil
When I Call this SP on MySQL directly fUPic have blob data and there is no problem on SP
I have tried SP.CreateBlobStream() but what I should pass to it`s first parameter as TField ?!
I have tried casting to TBlobStream from Param.AsBlob but no chance !
I want to know how I can get an OUT blob parameter as TStream ?
I`m using Delphi XE6 and UniDAC 6.1.4
Use AsBlob. It returns the blob as a byte array. You can either use that directly, or if you need to access the data via a stream do so with TBytesStream.
Stream := TBytesStream.Create(Param.AsBlob);

Golang Join array interface

I try to create bulk insert. I use gorm github.com/jinzhu/gorm
import (
"fmt"
dB "github.com/edwinlab/api/repositories"
)
func Update() error {
tx := dB.GetWriteDB().Begin()
sqlStr := "INSERT INTO city(code, name) VALUES (?, ?),(?, ?)"
vals := []interface{}{}
vals = append(vals, "XX1", "Jakarta")
vals = append(vals, "XX2", "Bandung")
tx.Exec(sqlStr, vals)
tx.Commit()
return nil
}
But I got an error:
Error 1136: Column count doesn't match value count at row 1 becuse i return wrong query
INSERT INTO city(code, name) VALUES ('XX1','Jakarta','XX2','Bandung', %!v(MISSING)),(%!v(MISSING), %!v(MISSING))
If I use manual query it works:
tx.Exec(sqlStr, "XX1", "Jakarta", "XX2", "Bandung")
It will generate:
INSERT INTO city(code, name) VALUES ('XX1', 'Jakarta'),('XX2', 'Bandung')
The problem is how to make array interface to generate string like "XX1", "Jakarta", ...
Thanks for help.
If you want to pass elements of a slice to a function with variadic parameter, you have to use ... to tell the compiler you want to pass all elements individually and not pass the slice value as a single argument, so simply do:
tx.Exec(sqlStr, vals...)
This is detailed in the spec: Passing arguments to ... parameters.
Tx.Exec() has the signature of:
func (tx *Tx) Exec(query string, args ...interface{}) (Result, error)
So you have to pass vals.... Also don't forget to check returned error, e.g.:
res, err := tx.Exec(sqlStr, vals...)
if err != nil {
// handle error
}

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.

Golang set a maps key as a variable for its value

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.