技术贴 | libpq SDK 发送 SQL 和解析结果

news/2025/1/8 19:42:39/

本次技术贴将详细讲解:当用户建立连接后,如何发送 SQL 语句、获取结果对象、查看错误信息等。

一、PGconn 对象

当用户通过 PQconnectdb, PQconnectdbParams, PQsetdbLogin 尝试与 PostgreSQL 服务器建立连接后,无论成功与否,libpq 会返回一个 PGconn 对象给用户,该对象封装了连接的信息,比如:

  • dbName — 数据库名称

  • pguser — 用户名

  • status — 连接状态

  • errorMessage — 错误信息

用户可以通过 PQstatus (<PGconn对象>) 获取到连接状态,如果状态为 CONNECTION_OK ,即说明已经成功连接数据库,并且状态健康;若连接失败,可以通过 PQerrorMessage (<PGconn对象>) 获取到错误的具体信息。

在之后发送 SQL Command 等操作中,都需要将 PGconn 对象作为入参才可以进行。

二、命令执行函数

在成功连接数据库并且获取到 PGconn 对象后,用户即可使用 PQexec 上传命令并且等待结果:

PGresult *PQexec(PGconn *conn, const char *command)

通过 PQexec 返回来的 PGresult 对象和 PGconn 是类似的逻辑:PGresult 里面封装了单个 SQL 命令的查询结果,比如:

  • Tuples — 元组

  • resultStatus — 结果状态

  • errMsg — 错误信息

用户可以通过 PQresultStatus (<PGresult 对象>) 获取到结果状态,如果状态为 PGRES_COMMAND_OK,说明已经成功执行了命令,但是没有返回任何的值;如果状态为 PGRES_TUPLES_OK,说明成功执行命令,并且返回值已经存在了 tuples 中。

if (PQresultStatus(*pgres) != PGRES_COMMAND_OK) {    //error handling  }

三、SQL 注入 PQExecParam

当数据库服务器被错误地引导,将查询的动态参数视为查询文本的一部分时,就会发生 SQL 注入。比如查询文本内容本身就是“DROP TABLE STUFENTS”,可能会产生 SQL 注入。

为避免这种情况,PostgreSQL 通过协议将动态参数作为单独的实体发送,PQExecParam 就是其中一种方法:

PGresult *PQexecParams(PGconn *conn,                       const char *command,                       int nParams,                       const Oid *paramTypes,                       const char *const *paramValues,                       const int *paramLengths,                       const int *paramFormats,                       int resultFormat);                    

PQExecParam 与 PQExec 很像,但提供了额外的功能:参数值可以与命令字符串本身分开指定,查询的结果也可以被指定为文本或者二进制。

  • conn:PGconn 对象

  • command: SQL 字符串命令

  • nParams: 提供的参数数量

  • paramTypes: 参数符号的数据类型

  • paramValues: 指定参数的实际值

  • paramLengths: 指定二进制格式参数的实际数据长度

  • paramFormats: 指定参数是文本还是二进制

  • resultFormat: 获取文本格式还是二进制格式结果

除了避免容易出错的引用和转义之外,PQExecParam 只允许在字符串中有最多一个 SQL 命令。

四、错误处理

当出现错误的时候,除了上文提到的 PQerrorMessage,也可以通过 PQresultErrorField 获取到 PGresult 的相应错误信息:

char *PQresultErrorField(const PGresult *res, int fieldcode)

fieldcode 是 libpq 定义的错误消息字段的标识符:

#define PG_DIAG_SEVERITY    'S'#define PG_DIAG_SQLSTATE    'C'#define PG_DIAG_MESSAGE_PRIMARY 'M'#define PG_DIAG_MESSAGE_DETAIL  'D'#define PG_DIAG_MESSAGE_HINT  'H'#define PG_DIAG_STATEMENT_POSITION 'P'#define PG_DIAG_INTERNAL_POSITION 'p'#define PG_DIAG_INTERNAL_QUERY  'q'#define PG_DIAG_CONTEXT      'W'#define PG_DIAG_SCHEMA_NAME    's'#define PG_DIAG_TABLE_NAME    't'#define PG_DIAG_COLUMN_NAME    'c'#define PG_DIAG_DATATYPE_NAME  'd'#define PG_DIAG_CONSTRAINT_NAME 'n'#define PG_DIAG_SOURCE_FILE    'F'#define PG_DIAG_SOURCE_LINE    'L'#define PG_DIAG_SOURCE_FUNCTION 'R'

比如用户可以通过 PQresultErrorField (pqres, PG_DIAG_SQLSTATE) 获取到返回的 PostgreSQL error code。

五、获取结果

如果获取到的结果对象状态良好,用户即可使用 PQntuples 获取列数,PQnfields 获取行数, PQfname 获取列名,PQgetvalue 获取某一行某一列的结果。

void printPGresult(PGresult *res) {  std::cout << PQntuples(res) << "tuples, " << PQnfields(res) << " fields"            << std::endl;  // print column name  for (int i = 0; i < PQnfields(res); i++) {    std::cout << PQfname(res, i) << "\t";    }    std::cout << std::endl;    // print column values    for (int i = 0; i < PQntuples(res); i++) {    for (int j = 0; j < PQnfields(res); j++) {      std::cout << PQgetvalue(res, i, j) << "\t";        }        std::cout << std::endl;  }}

在此次 PGresult 操作结束之后,无论是否成功获取到结果,都需要使用 PQclear (<PGresult对象>) 清理结果,以免出现内存泄露的问题。

六、总结

通过 libpq 与 PostgreSQL 建立连接之后,可以通过 libpq 所在的 client 端发送 SQL 接收结果,或获取错误信息。为了避免 SQL 注入的问题,用户可以使用相应的协议或者是函数避免错误的产生。


http://www.ppmy.cn/news/349862.html

相关文章

【离散数学】特殊图

目录 树的基本概念及性质 生成树及算法 根树 最优树和哈夫曼算法 欧拉图 欧拉图的判定 哈密顿图 偶图 匹配 平面图 欧拉公式 特殊图的应用 树的基本概念及性质 树是一种由节点和边构成的数据结构&#xff0c;它的特点是没有环路&#xff0c;每个节点恰好有一个父节…

电脑读卡器,笔记本读卡器怎么用教程

很多人可能不知道&#xff0c;其实笔记本电脑都自带读卡器的&#xff0c;也就是说我们不需要单独去买读卡器就能让我们的笔记本电脑识别我们的内存卡&#xff0c;包括相机里用的sd卡&#xff0c;我手机里用的TF卡都是可以被识别的&#xff0c;而且一般来说笔记本电脑自带的读卡…

RPA手把手:python 获取依赖包和安装依赖包

学Python ,用RPA&#xff0c;艺赛旗RPA 2020.1全新首发免费下载 点击下载 www.i-search.com.cn/index.html?fromline1 获取依赖包&#xff1a; 第一种方法&#xff1a;获取环境中所有安装的包 打开命令提示符&#xff0c;在某条路径下输入 pip freeze > ./requirements…

(RPA)学习 解锁屏的小尝试

貌似就不行了&#xff0c;反正我的测试环境&#xff08;Windows 2008 R2&#xff09;是这种情况就会不行了。经过 了解&#xff0c;自带的解锁屏使用的服务是WINIO服务。 那好&#xff0c;上网找了些关于python和WINIO的资料&#xff0c;发现可以喔&#xff0c;有个库叫做pywin…

RPA教学——判断(if)语句

了解RPA&#xff1a; www.i-search.com.cn 学习RPA https://support.i-search.com.cn/ 下载RPA&#xff1a; https://www.i-search.com.cn/?fromcsdn if 的三种形式 缩进打不出来&#xff0c;所以只能用缩进代替了 if if 条件 1: (缩进) 条件 1 满足执行的代码 &#xf…

UEBA 和 RPA 服务端不出现 license 的二维码解决思路

艺赛旗 RPA9.0全新首发免费下载 点击下载 http://www.i-search.com.cn/index.html?fromline1 详细内容请参看艺赛旗官网支持栏目&#xff1a;RPA社区 点击链接进入 http://support.i-search.com.cn/ 艺赛旗RPA支持社区QQ群&#xff1a;924320215 UEBA/RPA 服务器系统注册页面…

宏碁tc601bios_acer tc-601电脑biso中不存在LAUNCH CSM这个选项,无法将win8改装为win7,怎么办?...

这个情况要去官网下载32313133353236313431303231363533e4b893e5b19e31333365653235带开启CSM功能的bios升级文件。在产品支持里面有的。升级之后就可以进bios开启CSM了。升级bios有风险,自己注意。 DOS Flash Utility SOP v1.2 1. Copy BIOS folder to USB storage. 2. Power…

RPA中js的相关使用

【本文相关内容参考自金智维公司论坛文章&#xff0c;如有侵权&#xff0c;请联系删除】 1.JavaScript概述 1.1 JavaScript简介 1.2 JavaScript特征 1.3 JavaScript教程 2. JavaScript应用 2.1.标签查找 2.2元素赋值 2.3 触发事件 2.4 保存图片 2.5 异步处理 1. Java…