I've never used golang with mysql before, so I'm reading about these for the first time. I'd like to do something like this:
if userId && gender && age
db.QueryRow("SELECT name FROM users WHERE userId=? AND gender=? AND age=?", userId,gender,age)
else if gender && age
db.QueryRow("SELECT name FROM users WHERE gender=? AND age=?", gender, age)
else if userId && gender
db.QueryRow("SELECT name FROM users WHERE userId=? AND gender=?", userId,gender)
else if userId && age
db.QueryRow("SELECT name FROM users WHERE userId=? AND age=?", userId, age)
else if gender
db.QueryRow("SELECT name FROM users WHERE gender=?", gender)
else if userId
db.QueryRow("SELECT name FROM users WHERE userId=?", userId)
else if age
db.QueryRow("SELECT name FROM users WHERE age=?", age)
This is far too much typing, especially if I have a dozen more variables I'd like to add to the WHERE condition.
If this were PHP, I'd do something like this:
$sql = "SELECT name FROM users ";
$where = array();
foreach(explode(",","userId,gender,age,name,height,weight,ethnicity" as $field)
{
if(isset($arrayOfValues[$field]))
{
$where[count($where)] = $field." = ?".$field
$db->bind("?".$field,$arrayOfValues[$field]);
}
}
if(count($where)>0)
$sql = $sql . " WHERE ".implode(" AND ",$where);
$db->query($sql);
By using a foreach loop, I can dynamically generate queries and dynamically bind as many variables as needed.
Is something like this an option with golang and mysql? Or are there alternatives to not typing out every single variable combination for a query?
If you have a map with the field names and values like this:
m := map[string]interface{}{"UserID": 1234, "Age": 18}
then you can build the query like this:
var values []interface{}
var where []string
for _, k := range []string{"userId", "gender", "age", "name", "height", "weight", "ethnicity"} {
if v, ok := m[k]; ok {
values = append(values, v)
where = append(where, fmt.Sprintf("%s = ?", k))
}
}
r, err := db.QueryRow("SELECT name FROM users WHERE " + strings.Join(where, " AND "), values...)
This is not susceptible to SQL injection because placeholders are used for parts of the query outside the application's direct control.
If the map keys are known to be allowed field names, then use this:
var values []interface{}
var where []string
for k, v := range m {
values = append(values, v)
where = append(where, fmt.Sprintf("%s = ?", k))
}
r, err := db.QueryRow("SELECT name FROM users WHERE " + strings.Join(where, " AND "), values...)
Related
Using PHP sql pdo driver i can send multiple statements into a single query and get multiple row sets.
$sql = "insert into foo () values () ; insert into bar () values (), ()";
$stmt = $dbh->prepare($sql);
$stmt->execute();
do {
echo "row count: ".$stmt->rowCount()."\n";
} while ($stmt->nextRowset());
I am looking forward to achieve the same thing in Go.
I've tried below snippet to update a table. But when printing res.Next() and res.NextResultSet(), both are false while the rows are updated after my checking. So I cannot iterate the resultset to get the count.
tx, _ := conn.StartTransaction(ctx)
sqlstr := `update table1 set user_name = "d1" where user_id = 1;
update table1 set user_name = "d2" where user_id = 2;`
res, err := tx.Query(sqlstr)
if err != nil {
fmt.Println(err.Error())
}
fmt.Println(res.Next())
fmt.Println(res.NextResultSet())
For all of my GET requests, I'm getting an Internal Server Error 500. I'm not sure what I'm doing wrong, so maybe there's something that I don't understand or am looking over and need a second pair of eyes. Any help/hints are appreciated. My code is below:
/**
* Return all Users and their respective rows from users table
**/
func (user *User) GetAll() ([]User, *errors.RestErr) {
var (
currUser User
users []User
)
//queryGetAllUsers = "select id, first_name, last_name, email, username, password, created_on, updated_on FROM users;
rows, err := users_db.Client.Query(queryGetAllUsers)
if err != nil {
return nil, errors.NewInternalServerError(err.Error())
}
// loop through addresses of users returned from database and append column values to collection
for rows.Next() {
if err := rows.Scan(&user.ID, &user.FirstName, &user.LastName, &user.Email, &user.Username, &user.Password, &user.CreatedOn, &user.UpdatedOn); err != nil {
return nil, mysql_utils.ParseError(err)
}
users = append(users, currUser)
}
defer rows.Close()
if len(users) == 0 {
return nil, errors.NewNotFoundError("No users in table")
}
return users, nil
}
I have some html that allows the user to search with 2 criteria: name and cost. I am trying to make it so that the query displays results based on what the user enters for name and cost. There are 3 cases: name entered no cost, cost entered no name, both entered
SELECT name, cost FROM table WHERE...
I know to get the values entered by doing $_POST['name'] and $_POST['cost'] but I am struggling on how to set up the query to display the information based on the possible user input combinations
Is this what you basically want?
select . . .
from t
where (name = #name or #name is null) and
(cost = #cost or #cost is null);
Here #name and #cost are parameters used to pass the user input values into the query.
For my case, I always arrange the SQL like below:-
$nane = $_POST['name']:
$cost = $_POST['cost'];
if((isset($nane) && ($nane != "")) && (isset($cost) && ($cost != "")))
{
$sql = "select name,cost from table where columnNm ='".$nane."' and columnNm1 = ".$cost;
}
else if((isset($nane) && ($nane != "")) && (!isset($cost) && ($cost == "")))
{
$sql = "select name,cost from table where columnNm ='".$nane."'";
}
else if((!isset($nane) && ($nane == "")) && (isset($cost) && ($cost != "")))
{
$sql = "select name,cost from table where columnNm1 = ".$cost;
}
I solve my issues like this. If you think it's suitable for you then you may use it.
I'm trying to build a query as follows, here I'm using gorp.
func GetAgregatedDownloadsFromTo(constrains Constrains) [] dao.NameValue {
dbMap := utils.GetDBConnection("radsummary");
defer dbMap.Db.Close()
var totalDailyDownloads[] NameValue
query := "SELECT SUM(outputoctets) as value ,date as name FROM dailyacct where date >= ? AND date < ? "
if len(constrains.LocationGroups) > 0 {
query = query + " AND calledstationid=? "
for i := 1; i< len(constrains.LocationGroups); i++ {
query = query + " OR calledstationid=? "
}
query = query + " group by date"
print(query)
_, err := dbMap.Select(&totalDailyDownloads, query, constrains.From, constrains.To, constrains.LocationGroups...)
if err != nil {
panic(err.Error()) // proper error handling instead of panic
}
}
return totalDailyDownloads
}
type Constrains struct {
From string `json:"from"`
To string `json:"to"`
LocationGroups []string `json:"locationgroups"`
}
Query construction happens based on the length of constrains.LocationGroups. Trouble I'm having is passing the variable number of args to the Select query once I give constrains.LocationGroups... as select query parameters it throws a compiler error too many arguments in call to dbMap.Select
Is it possible to achieve this kind of requirement?. Appreciate your input.
Found an answer based on Pass string slice to variadic empty interface parameter
Below is the updated code to achieve the task
func GetAgregatedDownloadsFromTo(constrains dao.Constrains) [] dao.NameValue {
dbMap := utils.GetDBConnection("radsummary");
defer dbMap.Db.Close()
var totalDailyDownloads[] dao.NameValue
query := "SELECT SUM(outputoctets) as value ,date as name FROM dailyacct where date >= ? AND date < ? "
if len(constrains.LocationGroups) > 0 {
args := make([]interface{}, len(constrains.LocationGroups)+2)
args[0] = constrains.From
args[1] = constrains.To
for index, value := range constrains.LocationGroups { args[index+2] = value }
query = query + " AND calledstationid=? "
for i := 1; i< len(constrains.LocationGroups); i++ {
query = query + " OR calledstationid=? "
}
query = query + " group by date"
print(query)
_, err := dbMap.Select(&totalDailyDownloads, query, args...)
if err != nil {
panic(err.Error()) // proper error handling instead of panic
}
}
return totalDailyDownloads
}
Here I had to convert the string slice to an interface slice.
I've wrote an function (In Go, of course) that inserting map[string]interface{} to mysql via this library.
Explanation of the code below:
The functions receives string called table and map[string]interface{} called data.
I separate the data to keys (variable called columns) and values (variable called values).
I generate from the columns variable called column_text that will look like this: first_name, last_name, birth_day, date_added
I generate from the values variable called variable_text that will look like this: ?, ?, ?, ?
I open mysql connection: db, err := sql.Open("mysql", "user:pass#/database")
I create prepared statement: stmt, err := db.Prepare("INSERT INTO " + table + " ( " + columns_text + " ) VALUES ( " + values_text + " )")
I execute the prepare statement. but I have a problem. the number of the columns and values changes all the time, and the stmt.Exec() command can't receive an slice (Array) like this: stmt.Exec(values), only the values alone like this: stmt.Exec(values[0], values[1], values[2]...)
The question:
I'm coming from PHP, where PDO::Statement could receive an array when executing.
How can I execute the statement with the slice (Array)? (If I can do it with different library, please write the name of the library and how to use it, thank you!)
The code:
func insertToDB(table string, data map[string]interface{}) {
columns := make([]interface{}, 0, len(data))
values := make([]interface{}, 0, len(data))
for key, _ := range data {
columns = append(columns, key)
values = append(values, data[key])
}
columns_text := ""
i := 0
of := len(data)
for i < of {
column := columns[i].(string)
if i == 0 {
columns_text = column
} else {
columns_text = columns_text + ", " + column
}
i++
}
fmt.Println(columns_text + " = " + table)
values_text := ""
i = 0
for i < of {
if i == 0 {
values_text = "?"
} else {
values_text = values_text + ", ?"
}
i++
}
fmt.Println(values_text)
fmt.Println(values)
fmt.Println(data)
db, err := sql.Open("mysql", "root:root#/bacafe")
if err != nil {
return -1, err
}
defer db.Close()
stmtIns, err := db.Prepare("INSERT INTO " + table + " ( " + columns_text + " ) VALUES ( " + values_text + " )")
if err != nil {
return -1, err
}
defer stmtIns.Close() // Close the statement when we leave main() / the program terminates
result, err := stmtIns.Exec(values...)
if err != nil {
return -1, err
} else {
insertedID, err := result.LastInsertId()
if err != nil {
return -1, err
} else {
return int(insertedID), nil
}
}
}
EDIT: I've edited the function above and it works perfectly now.
Thank you!
You are on the right track however Stmt.Exec takes args ...interface{}, so for your specific example you need to change 2 things:
......
values := make([]interface{}, 0, len(data))
......
//adding ... expands the values, think of it like func.apply(this, array-of-values) in
// javascript, in a way.
_, err = stmtIns.Exec(values...)