用 psycopg2 安全操作 postgresql 的核心是禁用字符串拼接 sql,必须使用 %s 占位符配合元组传参实现参数化查询,确保用户输入被数据库引擎作为纯数据处理而非 sql 代码执行。

直接说结论:用 psycopg2 安全操作 PostgreSQL,核心不是“怎么连”,而是“别拼接 SQL 字符串”。几乎所有注入漏洞都出在这里。
为什么不能用 f-string 或 % 格式化拼接 SQL
因为用户输入一旦混进 SQL 字符串,就可能被解析为语句的一部分。比如用户名输入 ' OR '1'='1,拼成 SELECT * FROM users WHERE name = 'admin' OR '1'='1',整张表就暴露了。
psycopg2 的参数化查询会把值当作纯数据传给 PostgreSQL 服务端,由数据库引擎做类型校验和转义,根本不会走 SQL 解析器。
- ✅ 正确写法:
cursor.execute("SELECT * FROM users WHERE id = %s", (user_id,)) - ❌ 危险写法:
cursor.execute(f"SELECT * FROM users WHERE id = {user_id}") - ❌ 同样危险:
cursor.execute("SELECT * FROM users WHERE name = '%s'" % name)
如何正确传递多个参数和不同数据类型
psycopg2 支持 %s 占位符(注意:不是 Python 的字符串格式化,是 psycopg2 的协议占位符),所有参数必须以 tuple 或 list 传入,单个值也要加逗号写成 (value,)。
图片提示词生成器?不止如此。 马甲系统 —— 把脑海中的画面,翻译成AI能理解的专业表达。 用得越多,它越懂你:首次需要多问几句确认方向,用久了几乎一说就懂。 用得越多,它越快:缓存机制让后续对话越来越省。 RAG进化:成功案例持续入库,越跑越聪明。 输入「新手指南」查看完整功能介绍
- 多个值:
cursor.execute("INSERT INTO logs (path, status, duration) VALUES (%s, %s, %s)", ("/api/v1/users", 200, 12.5)) - None 值会被自动转为 SQL
NULL:cursor.execute("UPDATE posts SET published_at = %s WHERE id = %s", (None, 101)) - 日期、JSON、数组等复杂类型,只要 Python 对象能被 psycopg2 类型适配器识别,就不用手动序列化:
cursor.execute("INSERT INTO events (data) VALUES (%s)", ([{"a": 1}, {"b": 2}],))
别用 %d、%f 或命名占位符 %(name)s —— 后者虽支持,但容易和 Python 字典格式混淆,且在动态字段名场景下仍需拼接,反而增加风险。
事务控制与连接生命周期的常见陷阱
psycopg2 默认开启隐式事务,每个 execute() 都在事务中,但不显式 commit() 或 rollback(),连接关闭时会自动回滚未提交的更改——这常导致你以为写入成功,其实丢了数据。
- 写操作后务必调用
conn.commit(),尤其在长连接或连接池中 - 捕获异常时要
conn.rollback(),否则后续语句可能因事务状态异常而失败 - 不要长期持有
connection对象:它不是线程安全的;多线程必须用连接池(如psycopg2.pool.ThreadedConnectionPool)或每个线程单独建连 -
cursor.close()和conn.close()推荐显式调用,但更稳妥的是用上下文管理器:with psycopg2.connect(...) as conn:<br> with conn.cursor() as cur:<br> cur.execute(...)
退出块时自动 close + rollback(若未 commit)
最易被忽略的一点:批量插入时,executemany() 虽方便,但它内部仍是多次独立执行,不是单条 SQL。真要性能,得用 execute_batch()(psycopg 3)或 execute_values()(psycopg2.extras),但这些函数依然要求参数严格参数化——别以为“批量”就能绕过安全规则。
Python免费学习笔记(深入):立即使用
在学习笔记中,你将探索 Python 的核心概念和高级技巧!










