Golang中如何使用字符串准备查询语句?
Golang中如何使用字符串准备查询语句? 我正在努力寻找一种管理数百条SQL查询的方法。我发现使用fmt.Sprintf来“准备”查询可能是一个解决方案,但似乎失败了:
query = fmt.Sprintf(`SELECT job_id,job_name
FROM job WHERE job_id = $?`, val)
fmt.Println(query)
“query”的结果是这样的:
SELECT job_id,job_name
FROM job WHERE job_id = $?
%!(EXTRA string=20-0001)
使用它会导致错误:
rows, err := db.Queryx(query)
pq: syntax error at or near "$"
这是不可能的吗?还是有更聪明的方法来处理数百条查询?我需要一个准备好的语句吗?
更多关于Golang中如何使用字符串准备查询语句?的实战教程也可以访问 https://www.itying.com/category-94-b0.html
不,你需要自己处理。此外,这存在SQL注入的风险。
再次强调,你应该优先使用数据库真正的预编译语句。
更多关于Golang中如何使用字符串准备查询语句?的实战系列教程也可以访问 https://www.itying.com/category-94-b0.html
如果你想使用 fmt.Sprintf,你需要遵循 fmt 的语法,而不是 SQL 的语法。
不过,你应该优先使用真正的预处理语句,而不是格式化字符串。
NobbZ 的意思是,永远不要将参数值直接嵌入字符串来构建查询。你的 SELECT 语句应该像这样:
selectStmt := `SELECT job_id, job_name FROM job WHERE job_id = ?;`
假设你正在使用 database/sql 包,并且已经通过 sql.Open(或 sql.OpenDB 等)创建了一个 *sql.DB,那么你应该像这样执行你的 SQL:
func getJobIDAndName(ctx context.Context, db *sql.DB, id string) (id, name string, error) {
const selectStmt = `SELECT job_id, job_name FROM job WHERE job_id = ?;`
var jobName string
if err := db.QueryRowContext(ctx, selectStmt, id).Scan(&id, &jobName); err != nil {
return "", "", err
}
return id, jobName, nil
}
将参数值与实际的 SELECT 语句分开,可以确保驱动程序和数据库引擎将这些值作为值来处理。
最糟糕的做法是使用 fmt.Sprintf。下面是如何以一种可能导致你被降职、解雇、甚至因重大过失而被起诉等方式来实现 getJobIDAndName 函数:
func prayWeGetJobIDAndName(ctx context.Context, db *sql.DB, id string) (id, name string, error) {
const selectStmt = `SELECT job_id, job_name FROM job WHERE job_id = '%s';`
var jobName string
if err := db.QueryRowContext(ctx, fmt.Sprintf(selectStmt, id)).Scan(&id, &jobName); err != nil {
return "", "", err
}
return id, jobName, nil
}
如果你使用了这个版本,并且有人这样调用它(至少在 MS SQL 上是这样。我不知道其他语法):
id, name, err := prayWeGetJobIDAndName("'; drop table job; --")
那么 fmt.Sprintf 会将你的查询变成:
SELECT job_id, job_name FROM job WHERE job_id = ''; drop table job; --';
然后你的 job 表可能就没了。
当然,没有人会把这样的代码写进去。更可能的情况是,你构建了一个 Web 服务,期望有人通过 HTTP GET 请求获取一个 job ID,然后你使用这个 ID 来查询你的 job 表。他们可以利用这个恶意的 “job ID” 字符串来破坏你的数据。
如果你使用第一个 getJobIDAndName 函数实现,并尝试将 "'; drop table job; --" 作为 job ID 传递,它将在数据库服务器上执行如下(再次说明,我使用的是 MS SQL 语法):
SELECT job_id, job_name FROM job WHERE job_id = ' ''; drop table job; --';
这个查询中的单引号被转义了,因此整个 job_id 参数被当作一个参数处理。然后查询会返回没有找到任何结果。
TL;DR:***永远永远永远***不要使用 fmt.Sprintf 来填充 SQL 查询参数。



