使用PyQt5 QPSQL驱动调用PostgreSQL存储过程遇42601错误
PostgreSQL存储过程调用错误(QPSQL驱动42601语法错误)
问题排查与修复步骤
1. 存储过程定义的明显错误
你的存储过程存在三处关键问题:
- 参数
_report_date未指定数据类型,需补充匹配业务场景的类型(比如date) - 语言声明拼写错误:
plpgsqle→plpgsql - 调用名称不匹配:代码中调用
es_load_text_report,但定义的存储过程是es_edit_text_report
修正后的存储过程代码:
CREATE OR REPLACE PROCEDURE es_edit_text_report( IN _id integer, IN _report_date date, -- 补全数据类型 IN _responsibleid integer, IN _categoryid integer, IN _description varchar, IN _location varchar ) AS $$ BEGIN UPDATE text_maintenance_news SET report_date = _report_date, reporterid = _responsibleid::smallint, categoryid = _categoryid::smallint, description = _description, location = _location WHERE id = _id; -- 移除COMMIT:由调用端控制事务,避免与Qt的数据库上下文冲突 END; $$ LANGUAGE plpgsql; -- 修正拼写错误
2. QPSQL驱动预准备语句不支持CALL的解决方案
旧版本QPSQL驱动存在限制:无法通过PREPARE语句调用存储过程,这是触发ERROR: syntax error at or near "CALL"的核心原因。以下两种安全方式可替代拼接参数的不安全写法:
方案一:直接执行带命名参数的CALL语句
跳过prepare(),直接用exec()执行绑定好参数的语句:
def test(self): try: qry = QSqlQuery(self.db) # 匹配修正后的存储过程名称 sql = "CALL es_edit_text_report(:id, :report_date, :reporter, :category, :description, :location)" qry.bindValue(":id", QVariant(self.id)) qry.bindValue(":report_date", QVariant(self.reportDate.date().toString('yyyy-MM-dd'))) qry.bindValue(":reporter", QVariant(self.comboResponsible.getHiddenData(0))) qry.bindValue(":category", QVariant(self.comboCategory.getHiddenData(0))) qry.bindValue(":description", QVariant(self.txtDescription.text())) qry.bindValue(":location", QVariant(self.txtLocation.text())) if not qry.exec(sql): # 直接执行带绑定参数的SQL raise DataError("test: qry", qry.lastError().text()) except DataError as e: QMessageBox.warning(self, e.source, e.message, QMessageBox.Ok)
方案二:改用函数替代存储过程
如果驱动版本无法升级,可将存储过程改为返回受影响行数的函数,兼容预准备语句调用:
CREATE OR REPLACE FUNCTION es_edit_text_report_func( _id integer, _report_date date, _responsibleid integer, _categoryid integer, _description varchar, _location varchar ) RETURNS integer AS $$ BEGIN UPDATE text_maintenance_news SET report_date = _report_date, reporterid = _responsibleid::smallint, categoryid = _categoryid::smallint, description = _description, location = _location WHERE id = _id; RETURN FOUND; -- 返回是否更新成功 END; $$ LANGUAGE plpgsql;
调用代码(支持prepare):
def test(self): try: qry = QSqlQuery(self.db) qry.prepare("SELECT es_edit_text_report_func(:id, :report_date, :reporter, :category, :description, :location)") qry.bindValue(":id", QVariant(self.id)) qry.bindValue(":report_date", QVariant(self.reportDate.date().toString('yyyy-MM-dd'))) qry.bindValue(":reporter", QVariant(self.comboResponsible.getHiddenData(0))) qry.bindValue(":category", QVariant(self.comboCategory.getHiddenData(0))) qry.bindValue(":description", QVariant(self.txtDescription.text())) qry.bindValue(":location", QVariant(self.txtLocation.text())) if not qry.exec(): raise DataError("test: qry", qry.lastError().text()) except DataError as e: QMessageBox.warning(self, e.source, e.message, QMessageBox.Ok)
3. 额外注意事项
- 升级Qt版本至5.15+:新版本QPSQL驱动已优化对PostgreSQL存储过程的支持
- 检查数据库连接配置:确保使用与PostgreSQL版本匹配的驱动
- 始终使用参数绑定:禁止手动拼接SQL参数,避免SQL注入风险
内容的提问来源于stack exchange,提问作者Erick
相关产品推荐
相关产品推荐

