乐趣区

关于数据库:libpq-SDK-发送-SQL-和解析结果

本次技术贴将具体解说:当用户建设连贯后,如何发送 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 注入的问题,用户能够应用相应的协定或者是函数防止谬误的产生。

退出移动版