pgx驱动操作PostgreSQL时PlaceholderFormat未替换$参数占位符问题
问题根因说明
你观察到的pgAdmin日志中$占位符未被替换属于PostgreSQL参数化查询的正常机制,参数是和SQL模板分开传输的,不会拼接到SQL字符串中,这是数据库防SQL注入的原生设计,不是问题。
你实际遇到的查询失败是代码逻辑错误导致的,具体问题和修正方案如下:
代码存在的问题
- 执行SQL拿到rows结果后,没有调用
rows.Next()就直接执行Scan方法,永远无法读取到数据,会直接返回sql.ErrNoRows,你误以为是占位符替换失败导致的查询无结果 - 执行查询拿到
sqlerr之后,错误地判断了之前JSON解析的err变量,没有正确处理数据库查询的错误 - 用完
rows对象后没有调用Close方法,会造成数据库连接泄漏 - squirrel构造查询的写法冗余,不需要手动写
$1占位符,squirrel配置sq.Dollar格式后会自动转换占位符
修正后的代码
func Signin(c *gin.Context) { // 解析请求参数 creds := &Credentials{} err := json.NewDecoder(c.Request.Body).Decode(creds) if err != nil { c.Writer.WriteHeader(http.StatusBadRequest) return } // 构造查询语句,用?做占位符,squirrel会自动转成$1格式 query, args, err := sq. Select("password"). From("users"). Where("username = ?", creds.Username). PlaceholderFormat(sq.Dollar). ToSql() if err != nil { c.Writer.WriteHeader(http.StatusInternalServerError) return } // 单条查询用QueryRow更合适 storedCreds := &Credentials{} err = db.QueryRow(query, args...).Scan(&storedCreds.Password) if err != nil { if err == sql.ErrNoRows { c.Writer.WriteHeader(http.StatusUnauthorized) return } c.Writer.WriteHeader(http.StatusInternalServerError) return } // 校验密码 if err = bcrypt.CompareHashAndPassword([]byte(storedCreds.Password), []byte(creds.Password)); err != nil { c.Writer.WriteHeader(http.StatusUnauthorized) return } c.Writer.WriteHeader(http.StatusOK) }
额外优化建议
- 可以直接用pgx的原生驱动,不需要通过database/sql的stdlib封装,性能更好
- 密码校验逻辑建议加常量时间比对的处理,避免计时攻击
- 数据库操作建议加超时控制,避免慢查询卡住请求
内容的提问来源于stack exchange,提问作者Paul Ryan
相关产品推荐
相关产品推荐

