不能在db.Query的参数中使用缓冲区(类型为bytes.Buffer)作为类型字符串

时间:2019-05-29 10:00:28

标签: go

我已经在buffer.WriteString()中编写了一个SQL查询,但是无法在db.Query()中使用该缓冲区。

buffer.WriteString(fmt.Sprintf(`SELECT c.id, c.company_name, ss.start_date, ss.shift_length, ss.bill_rate, ss.ot_hrs, ss.dt_hrs, ts.pay_rate, ts.wc_rate, ts.paid, td.wc
        FROM company c
        JOIN users u1 ON c.id = u1.company_id
        JOIN schedule s ON u1.id = s.user_id
        JOIN schedule_shifts ss ON s.id = ss.schedule_id
        JOIN technician_shifts ts ON ss.id = ts.shift_id
        JOIN users u ON u.id = ts.technician_id
        JOIN technician_details td ON td.user_id = u.id
        JOIN master_technicians mt ON mt.id = u.master_technician_id
        WHERE mt.id = %d AND ts.confirmed = 'yes' AND ts.paid = 'yes'`, masterID))

    if e.Type == "today" {
        buffer.WriteString(fmt.Sprintf(`AND ts.paid_on = CURDATE()`))
    } else if e.Type == "weekly" {
        buffer.WriteString(fmt.Sprintf(`AND ts.paid_on > DATE_SUB(NOW(), INTERVAL 1 WEEK)`))
    } else if e.Type == "monthly" {
        buffer.WriteString(fmt.Sprintf(`AND ts.paid_on > DATE_SUB(NOW(), INTERVAL 1 MONTH)`))
    } else {
        if e.StartDate != "" && e.EndDate != "" {
            if e.StartDate == e.EndDate {
                buffer.WriteString(fmt.Sprintf(`AND ts.paid_on = %s`, e.StartDate))
            } else {
                buffer.WriteString(fmt.Sprintf(`AND ts.paid_on >= %s AND ts.paid_on <= %s`, e.StartDate, e.EndDate))
            }
        }
    }

rows, err := db.Query(buffer)
    if err != nil {
        log.Panic(err.Error())
    }

它给我的错误是

  

不能在参数中使用缓冲区(类型为bytes.Buffer)作为类型字符串   db.Query

我该怎么做?

1 个答案:

答案 0 :(得分:2)

您的问题的直接答案是provided by the docs,只需使用String()方法即可:

rows, err := db.Query(buffer.String())

话虽如此,缓冲区在这里可能没有意义。对于构建字符串,strings.Builder类型更有效。

但是对于您的特定用例,以这种方式构建SQL查询既危险又容易出错。几点建议:

  1. 从不,从不使用字符串连接或Sprintf / Fprintf将值插入SQL查询。您将可以接受SQL注入攻击。相反,请始终使用参数化查询。
  2. 大型if / else块容易出错,很难阅读。请改用正确的switch语句。
  3. 您可以使用SQL查询构建器,但是在您的情况下,查询只有一个可选部分。只需为此使用标准字符串连接即可。

考虑到上述问题,我已经重写了您的代码,现在看起来像这样:

var paidCondition string
args := []interface{}{masterID}
switch (
case e.Type == "today":
    paidCondition = `AND ts.paid_on = CURDATE()`
case e.Type == "weekly":
    paidCondition = `AND ts.paid_on > DATE_SUB(NOW(), INTERVAL 1 WEEK)`
case e.Type == "monthly":
    paidCondition = `AND ts.paid_on > DATE_SUB(NOW(), INTERVAL 1 MONTH)`
case e.StartDate != "" && e.EndDate != "" && e.StartDate == e.EndDate:
    paidCondition = `AND ts.paid_on = ?`
    args = append(args, e.StartDate)
case e.StartDate != "" && e.EndDate != "":
    paidCondition = `AND ts.paid_on >= ? AND ts.paid_on <= ?`
    args = append(args, e.StartDate, e.EndDate)
)

query := `SELECT c.id, c.company_name, ss.start_date, ss.shift_length, ss.bill_rate, ss.ot_hrs, ss.dt_hrs, ts.pay_rate, ts.wc_rate, ts.paid, td.wc
    FROM company c
    JOIN users u1 ON c.id = u1.company_id
    JOIN schedule s ON u1.id = s.user_id
    JOIN schedule_shifts ss ON s.id = ss.schedule_id
    JOIN technician_shifts ts ON ss.id = ts.shift_id
    JOIN users u ON u.id = ts.technician_id
    JOIN technician_details td ON td.user_id = u.id
    JOIN master_technicians mt ON mt.id = u.master_technician_id
    WHERE mt.id = ? AND ts.confirmed = 'yes' AND ts.paid = 'yes' ` + paidCondition

rows, err := db.Query(query, args...)
if err != nil {
    log.Panic(err.Error())
}

最后,还有其他一些改进,这些改进与您的问题或我上面建议的代码没有直接关系:

  1. buffer.WriteString(fmt.Sprintf( ... ))过于冗长。较短的版本是:fmt.Fprintf(buffer, ... )
  2. 您在没有进行格式化的多个地方使用fmt.Sprintf()。只需将其完全删除。例如,将fmt.Sprintf("AND ts.paid_on = CURDATE()")替换为"AND ts.paid_on = CURDATE()"