0%
PostgreSQL分析#004#客户端开发指南

前面几篇博文,我们成功安装了数据库,本文开始我们介绍使用C和C++的开发接口,连接数据库和读取数据的方法。

修改PostgreSQL配置文件

在使用我们写的客户端程序连接数据库之前,我们需要修改服务端配置文件完成如下两次调整:

  • 第一,服务端默认监听在 127.0.0.1:5432端口上,其他主机无法通过局域网连接,需要修改 /etc/postgresql/14/main/postgresql.conf 文件,使得服务监听在对外端口上。
  • 第二,服务端默认允许本机可以通过网络连接 127.0.0.1:5432 端口,但即使我们按上述方法开放了对外端口,其他外部主机也无法连接上,我们还修改 /etc/postgresql/14/main/pg_hba.conf ,以开通外部连接权限。

针对第一处修改,在 /etc/postgresql/14/main/postgresql.conf 中修改 listen_addresses = 'localhost' 配置项,修改后如下图所示:

针对第二处修改,在 /etc/postgresql/14/main/postgresql.conf 配置文件中增加如下内容:

1
host    all    all    0.0.0.0/0    scram-sha-256

修改后如下图所示:

上述两处修改之后,使用如下命令重启PostgreSQL服务端:

1
$ sudo systemctl restart postgresql

C开发接口

libpq是PostgreSQL官方提供的开发库,在Ubuntu服务器上我们使用如下命令安装:

1
$ sudo apt install libpq-dev

安装成功之后,我们看下libpq-dev开发包含了哪些文件,使用如下命令查看:

mancode@manos:/var/lib/dpkg/info$ cat /var/lib/dpkg/info/libpq-dev.list 
/.
/usr
/usr/bin
/usr/bin/pg_config
/usr/include
/usr/include/postgresql
/usr/include/postgresql/internal
/usr/include/postgresql/internal/c.h
/usr/include/postgresql/internal/libpq
/usr/include/postgresql/internal/libpq/pqcomm.h
/usr/include/postgresql/internal/libpq-int.h
/usr/include/postgresql/internal/port.h
/usr/include/postgresql/internal/postgres_fe.h
/usr/include/postgresql/internal/pqexpbuffer.h
/usr/include/postgresql/libpq
/usr/include/postgresql/libpq/libpq-fs.h
/usr/include/postgresql/libpq-events.h
/usr/include/postgresql/libpq-fe.h
/usr/include/postgresql/pg_config.h
/usr/include/postgresql/pg_config_ext.h
/usr/include/postgresql/pg_config_manual.h
/usr/include/postgresql/pg_config_os.h
/usr/include/postgresql/postgres_ext.h
/usr/lib
/usr/lib/x86_64-linux-gnu
/usr/lib/x86_64-linux-gnu/libpq.a
/usr/lib/x86_64-linux-gnu/pkgconfig
/usr/lib/x86_64-linux-gnu/pkgconfig/libpq.pc
/usr/share
/usr/share/doc
/usr/share/doc/libpq-dev
/usr/share/doc/libpq-dev/copyright
/usr/share/man
/usr/share/man/man1
/usr/share/man/man1/pg_config.1.gz
/usr/lib/x86_64-linux-gnu/libpq.so
/usr/share/doc/libpq-dev/changelog.Debian.gz
mancode@manos:/var/lib/dpkg/info$ 

可见,该开发库包含了我们需要的头文件、静态库和动态库。

安装成功之后,我们使用如下 测试用例文件 来连接数据库以及从数据表t_map中读取数据。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
#include <stdio.h>
#include <stdlib.h>
#include <libpq-fe.h>

// gcc -o pgtest pgtest.c -I/usr/include/postgresql -lpq

int main(int argc, char **argv)
{
const char *conninfo;
PGconn *conn;
PGresult *res;

/* 连接数据库 */
conn = PQconnectdb("dbname=db_mancode user=user_mancode password=123456 hostaddr=127.0.0.1 port=5432");

/* Check to see that the backend connection was successfully made */
if (PQstatus(conn) != CONNECTION_OK)
{
fprintf(stderr, "Connection to database failed: %s", PQerrorMessage(conn));

PQfinish(conn);

return 1;
}

/* 开始检索数据,开启一个事务 */
res = PQexec(conn, "BEGIN");
if (PQresultStatus(res) != PGRES_COMMAND_OK)
{
fprintf(stderr, "BEGIN command failed: %s", PQerrorMessage(conn));
PQclear(res);
PQfinish(conn);

return 1;
}
PQclear(res);

res = PQexec(conn, "DECLARE myportal CURSOR FOR SELECT * FROM t_map");
if (PQresultStatus(res) != PGRES_COMMAND_OK)
{
fprintf(stderr, "DECLARE CURSOR failed: %s", PQerrorMessage(conn));
PQclear(res);
PQfinish(conn);

return 1;
}
PQclear(res);

res = PQexec(conn, "FETCH ALL in myportal");
if (PQresultStatus(res) != PGRES_TUPLES_OK)
{
fprintf(stderr, "FETCH ALL failed: %s", PQerrorMessage(conn));
PQclear(res);
PQfinish(conn);

return 1;
}

/* 先打印表头 */
int iFields = PQnfields(res);
for (int i = 0; i < iFields; i++)
{
printf("%-15s", PQfname(res, i));
}
printf("\n");

/* 再打印数据 */
for (int i = 0; i < PQntuples(res); i++)
{
for (int j = 0; j < iFields; j++)
{
printf("%-15s", PQgetvalue(res, i, j));
}
printf("\n");
}

PQclear(res);

/* 关闭游标 */
res = PQexec(conn, "CLOSE myportal");
PQclear(res);

/* 结束事务 */
res = PQexec(conn, "END");
PQclear(res);

/* 关闭连接 */
PQfinish(conn);

return 0;
}

编译测试用例,使用如下命令:

1
$ gcc -o pgtest pgtest.c `pkg-config libpq -libs -cflags`

或者

1
$ gcc -o pgtest pgtest.c -I/usr/include/postgresql -lpq

在服务器上运行程序,结果如下图所示:

C++开发接口

libpqxx是 PostgreSQL 的官方C++客户端 API,其源代码在BSD许可下使用,但其并不是Ubuntu的官方维护软件包。我们安装libpqxx可以通过命令或者自行编译安装,本文我们使用apt来安装。

1
sudo apt install libpqxx-dev

安装之后,我们使用C++来重写上面的 测试用例文件,如下文所示:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
#include <iostream>
#include <pqxx/pqxx>

// g++ -o pgtest pgtest.cpp -I/usr/include/postgresql -lpqxx

int main(int argc, char* argv[])
{
try
{
pqxx::connection C("dbname=db_mancode user=user_mancode password=123456 hostaddr=192.168.0.64 port=5432");

if (C.is_open())
{
std::cout << "Opened database successfully: " << C.dbname() << std::endl << std::endl;
}
else
{
std::cout << "Can't open database" << std::endl;
return 1;
}

pqxx::nontransaction N(C);

pqxx::result R(N.exec("SELECT * FROM t_map"));

for (pqxx::result::const_iterator c = R.begin(); c != R.end(); ++c)
{
std::cout << "name = " << c[0].as<std::string>() << std::endl;
std::cout << "level = " << c[1].as<std::string>() << std::endl << std::endl;
}

C.disconnect ();
}
catch (const std::exception &e)
{
std::cerr << e.what() << std::endl;

return 1;
}

return 0;
}

编译测试用例,使用如下命令:

1
$ g++ -o pgtest pgtest.cpp `pkg-config libpqxx -libs -cflags`

或者

1
$ g++ -o pgtest pgtest.cpp -I/usr/include/postgresql -lpqxx

在服务器上运行程序,结果如下图所示:

参考资料

https://www.postgresql.org/docs/16/libpq.html
https://www.postgresql.org/docs/16/libpq-example.html
https://www.jiyik.com/w/postgresql/postgresql-c-cpp