PyGreSQL query_formatted查询Redshift报Invalid type:705错误如何解决
错误成因
- 报错里提到的
Invalid type: 705,对应PostgreSQL协议中OID为705的unknown类型,也就是没有被显式声明数据类型的常量的默认标记。Redshift内部在处理这类unknown类型列的查询计划时,存在代码逻辑缺陷,没有做兼容判断就直接触发了底层断言,抛出了上下文信息极少的1000号内部错误。 - 这个unknown类型的出现,本质是混淆了
query_formatted的适用场景:这个方法的设计目标是处理SQL语句里值类参数的安全转义,它会自动给传入的字符串参数加单引号、转义内部特殊字符,最终输出SQL层面的字符串字面量,从机制上就不适合用来替换列名、表名、排序方向这类SQL结构标识符。 - 原本的代码意图是让占位符替换成无引号的列名
id、event_time,但query_formatted实际生成的SQL是把两个字符串当字面量处理后的版本:select 'id', 'event_time' from my.table limit 10。刚好这类SELECT列表里的无类型字符串常量触发了Redshift的内部bug,才导致了这个看不懂的断言错误。
排查与优化方案
- 根因验证方法:
- 开启PyGreSQL的查询日志,直接查看实际发送给Redshift的最终SQL语句,就能直观看到列名被单引号包裹的实际情况。
- 直接在Redshift客户端执行
select 'id', 'event_time' from my.table limit 10,大概率可以复现完全一致的报错,不需要死磕stl_error系统表的有限日志。
- 优化实现逻辑:
- 绝对不要用
query_formatted拼接列名、表名这类SQL结构元素,这种用法不仅会触发当前的类型错误,还会直接引入SQL注入风险。 - 动态选择列名的正确做法:先维护一份允许查询的列名白名单,对所有需要拼接进SQL的列名做合法性校验,不在白名单内的列名直接拦截,再通过字符串拼接的方式把校验通过的列名嵌入SQL结构部分。示例代码:
# 定义可查询的列白名单 ALLOWED_COLUMNS = {"id", "event_time", "user_id"} target_cols = ["id", "event_time"] # 逐列做合法性校验 for col in target_cols: if col not in ALLOWED_COLUMNS: raise ValueError(f"非法查询列: {col}") # 拼接带合法列名的SQL语句 sql = f"select {', '.join(target_cols)} from my.table limit 10" # 执行查询 res = c.query(sql).getresult()[0] - 如果后续需要用
query_formatted传字符串类的值参数,可以在占位符后显式标记类型(比如%s::varchar),强制服务端把参数识别为明确的字符串类型,从根源上避免unknown类型触发Redshift的内部断言错误。
- 绝对不要用
内容的提问来源于stack exchange,提问作者Vzzarr
相关产品推荐
相关产品推荐

