PostgreSQL C++客户端API libpqxx的使用

      PostgreSQL是一个先进的、开源的、免费的、功能强大的关系型数据库,缩写版是postgres。它使用一种客户端/服务器(C/S)的模型。一次PostgreSQL会话由下列相关的进程(程序)组成:

      (1).一个服务器进程,它管理数据库文件、接受来自客户端应用与数据库的连接并且代表客户端在数据库上执行操作。该数据库服务器程序叫做postgres。

      (2).那些需要执行数据库操作的用户的客户端(前端)应用。客户端应用可能本身就是多种多样的:可以是一个面向文本的工具,也可以是一个图形界面的应用,或者是一个通过访问数据库来显示网页的网页服务器,或者是一个特制的数据库管理工具。

      和典型的客户端/服务器应用一样,这些客户端和服务器可以在不同的主机上。这时它们通过TCP/IP网络连接通讯

      PostgreSQL服务器可以处理来自客户端的多个并发请求。因此,它为每个连接启动("forks")一个新的进程。从这个时候开始,客户端和新服务器进程就不再经过最初的postgres进程的干涉进行通讯。因此,守护者服务器进程总是在运行并等待着客户端连接,而客户端和相关联的服务器进程则是起起停停。

      一台运行着的PostgreSQL服务器可以管理许多数据库。通常我们会为每个项目和每个用户单独使用一个数据库。

      libpqxx是PostgreSQL的官方C++客户端API,源码地址:https://github.com/jtv/libpqxx,最新发布版本为7.9.2,license为BSD-3-Clause。

      Windows10上使用CMake编译libpqxx:

      1.build.sh脚本内容如下:

#! /bin/bash

if [ $# != 2 ]; then
    echo "Error: requires two parameters: 1: dynamic or static; 2: Relese or Debug"
    echo "For example: dynamic library, debug mode: $0  dynamic Debug"
    exit -1
fi

if [ $1 != "dynamic" ] && [ $1 != "static" ]; then
    echo "Error: the first parameter can only be dynamic or static"
    exit -1
fi

if [ $2 != "Release"  ] && [ $2 != "Debug" ]; then
    echo "Error: the second parameter can only be Release or Debug"
    exit -1
fi

if [[ ! -d "build" ]]; then
    mkdir build
    cd build
else
    cd build
fi

dynamic_flag=ON
if [ $1 != "dynamic" ]; then
    dynamic_flag=OFF
fi

echo "build: $1 $2"

# vs2022, PostgreSQL 16.4
 cmake \
    -G"Visual Studio 17 2022" -A x64 \
    -DPostgreSQL_ROOT="D:/ProgramFiles/PostgreSQL/16" \
    -DCMAKE_BUILD_TYPE=$2 \
    -DCMAKE_CONFIGURATION_TYPES=$2 \
    -DBUILD_SHARED_LIBS=${dynamic_flag} \
    -DCMAKE_INSTALL_PREFIX=./install \
    ..
make -j2
cmake --build . --target install --config $2

      2.注意:

      (1).编译libpqxx需要安装PostgreSQL,参考:https://blog.csdn.net/fengbingchun/article/details/141750748,或者至少需要安装用于客户端开发的C头文件和库。库libpqxx建立在PostgreSQL的标准C API libpq之上。

      (2).7.x版本至少需要C++17,对于8.x至少需要C++20。

      (3).推荐:windows上使用动态库,其它平台使用静态库。

      以下为测试代码段:

      (1).创建数据库:

int create_database(const std::string& dbname)
{
	try {
		pqxx::connection c{ options + "postgres" }; // database postgres must already exist
		c.set_client_encoding("GBK");
		pqxx::nontransaction tx{ c };
		const std::string str{ "CREATE DATABASE " };
		tx.exec(str + dbname);
	}
	catch (const std::exception& e) {
		std::cerr << "Error:create database: " << e.what() << std::endl;
		outfile << "Error:create database: " << e.what() << std::endl;
		return -1;
	}

	return 0;
}

      (2).连接数据库:

int connect_database(const std::string& dbname)
{
	try {
		pqxx::connection c{ options + dbname };
		//c.set_client_encoding("GBK"); // GBK, default:UTF8
		if (c.is_open())
			std::cout << c.dbname() << " database was opened successfully" << std::endl;
		else {
			std::cerr << "Error:failed to open database:" << dbname << std::endl;
			return -1;
		}

		std::cout << "dbname:" << c.dbname() << ", username:" << c.username()
			<< ", hostname:" << c.hostname() << ", port:" << c.port()
			<< ", backendpid:" << c.backendpid() << ", sock:" << c.sock()
			<< ", protocol_version:" << c.protocol_version()
			<< ", server_version:" << c.server_version()
			<< ", get_client_encoding:" << c.get_client_encoding()
			<< ", encoding_id:" << c.encoding_id()
			<< ", get_notifs:" << c.get_notifs()
			<< std::endl;
	}
	catch (const std::exception& e) {
		std::cerr << "Error:connect database: " << e.what() << std::endl;
		outfile << "Error:connect database: " << e.what() << std::endl;
		return -1;
	}

	return 0;
}

      (3).删除数据库:

int drop_database(const std::string& dbname)
{
	try {
		pqxx::connection c{ options + "postgres" };
		c.set_client_encoding("GBK");
		pqxx::nontransaction tx{ c };
		const std::string str{ "DROP DATABASE " };
		tx.exec(str + dbname);
	}
	catch (const std::exception& e) {
		std::cerr << "Error:drop database: " << e.what() << std::endl;
		outfile << "Error:drop database: " << e.what() << std::endl;
		return -1;
	}

	return 0;
}

      (4).创建表:

int create_table(const std::string& dbname, const std::string& command)
{
	try {
		pqxx::connection c{ options + dbname };
		c.set_client_encoding("GBK");
		pqxx::work w(c);
		w.exec(command);
		w.commit();
	}
	catch (const std::exception& e) {
		std::cerr << "Error:create table: " << e.what() << std::endl;
		outfile << "Error:create table: " << e.what() << std::endl;
		return -1;
	}

	return 0;
}

      (5).删除表:

int drop_table(const std::string& dbname, const std::string& tablename)
{
	try {
		pqxx::connection c{ options + dbname };
		c.set_client_encoding("GBK");
		pqxx::work w(c);
		const std::string str{ "DROP TABLE " };
		w.exec(str + tablename);
		w.commit();
	}
	catch (const std::exception& e) {
		std::cerr << "Error:drop table: " << e.what() << std::endl;
		outfile << "Error:drop table: " << e.what() << std::endl;
		return -1;
	}

	return 0;
}

      (6).向表中插入数据:

int insert_into_teacher(const std::string& dbname, int id, const std::string& name, const std::string& addr, const std::string& sex)
{
	try {
		pqxx::connection c{ options + dbname + " options='-c client_encoding=GBK'" }; // 有中文时需设置client_encoding=GBK,否则数据库中会显示乱码,推荐使用:c.set_client_encoding("GBK")
		pqxx::work w(c);

		const std::string query{ "INSERT INTO teacher (id, name, addr, sex) VALUES ($1, $2, $3, $4)" };
		w.exec_params(query, id, name, addr, sex);
		w.commit();
	}
	catch (const std::exception& e) {
		std::cerr << "Error:insert into: " << e.what() << std::endl;
		outfile << "Error:insert into: " << e.what() << std::endl;
		return -1;
	}

	return 0;
}

std::string get_current_time()
{
	auto timenow = std::chrono::system_clock::to_time_t(std::chrono::system_clock::now());
	std::tm* now_tm = std::localtime(&timenow);
	std::ostringstream oss;
	oss << std::put_time(now_tm, "%Y-%m-%d %H:%M:%S");
	return oss.str();
}

int insert_into_student(const std::string& dbname, int id, const std::string& name, const std::string& addr, int age,
	float score, const std::string& date, const std::string& sex)
{
	try {
		pqxx::connection c{ options + dbname };
		c.set_client_encoding("GBK"); // 有中文时需设置client_encoding=GBK,否则数据库中会显示乱码
		pqxx::work w(c);

		const std::string query{ "INSERT INTO student (id, name, addr, age, score, date, sex) VALUES ($1, $2, $3, $4, $5, $6, $7)" };
		w.exec_params(query, id, name, addr, age, score, date, sex);
		w.commit();
	}
	catch (const std::exception& e) {
		std::cerr << "Error:insert into: " << e.what() << std::endl;
		outfile << "Error:insert into: " << e.what() << std::endl;
		return -1;
	}

	return 0;
}

      (7).向已有表中添加、删除列:

int alter_table(const std::string& dbname)
{
	try {
		pqxx::connection c{ options + dbname };
		c.set_client_encoding("GBK");
		pqxx::work w(c);

		const std::string query{ "ALTER TABLE teacher " };
		w.exec(query + "ADD COLUMN hobbies CHAR(64) DEFAULT '游泳'");
		w.exec(query + "DROP COLUMN addr");
		w.commit();
	}
	catch (const std::exception& e) {
		std::cerr << "Error:alter table: " << e.what() << std::endl;
		outfile << "Error:alter table: " << e.what() << std::endl;
		return -1;
	}
	return 0;
}

      (8).删除表中数据:

int delete_table_data(const std::string& dbname)
{
	try {
		pqxx::connection c{ options + dbname };
		c.set_client_encoding("GBK");
		pqxx::work w(c);

		const std::string query{ "DELETE FROM teacher WHERE id > 10000 OR sex='女'" };
		w.exec(query);
		w.commit();
	}
	catch (const std::exception& e) {
		std::cerr << "Error:delete: " << e.what() << std::endl;
		outfile << "Error:delete: " << e.what() << std::endl;
		return -1;
	}

	return 0;
}

      (9).调整表中已有数据:

int update_table_data(const std::string& dbname)
{
	try {
		pqxx::connection c{ options + dbname };
		c.set_client_encoding("GBK");
		pqxx::work w(c);

		const std::string query{ "UPDATE teacher SET " };
		w.exec(query + "sex='男' WHERE sex='man'");
		w.exec(query + "sex='女' WHERE sex='woman'");
		w.commit();
	}
	catch (const std::exception& e) {
		std::cerr << "Error:update: " << e.what() << std::endl;
		outfile << "Error:update: " << e.what() << std::endl;
		return -1;
	}

	return 0;
}

      (10).查询表中数据:

int select_table_data(const std::string& dbname)
{
	try {
		pqxx::connection c{ options + dbname };
		c.set_client_encoding("GBK");
		pqxx::work w(c);

		const std::string query{ "SELECT * FROM student WHERE age >= 20 AND date > '2024-10-25 16:43:06'" };
		pqxx::result r(w.exec(query));
		w.commit();

		std::cout << "empty:" << r.empty() << ", size:" << r.size() << ", columns:" << r.columns() << std::endl;
		std::cout << "column name: ";
		for (auto i = 0; i < r.columns(); ++i)
			std::cout << r.column_name(i) << "," << r.column_type(r.column_name(i)) << ";";
		std::cout << std::endl;

		for (const auto& row : r) {
			for (const auto& field : row) {
				std::cout << field.c_str() << "\t";
			}
			std::cout << std::endl;
		}
	}
	catch (const std::exception& e) {
		std::cerr << "Error:select: " << e.what() << std::endl;
		outfile << "Error:select: " << e.what() << std::endl;
		return -1;
	}

	return 0;
}

      全局变量设置如下:

const std::string options{ "hostaddr=127.0.0.1 port=5432 user=postgres password=spring dbname=" };
const std::string table_teacher{ "CREATE TABLE teacher ("
						"id INTEGER PRIMARY KEY NOT NULL CHECK(id>1000),"
						"name CHAR(32) NOT NULL,"
						"addr CHAR(64) DEFAULT 'BeiJing',"
						"sex CHAR(8) NOT NULL)" };
const std::string table_student{ "CREATE TABLE student ("
						"id INTEGER PRIMARY KEY NOT NULL,"
						"name CHAR(32) NOT NULL,"
						"addr CHAR(64) DEFAULT 'TianJin',"
						"age INTEGER,"
						"score REAL,"
						"date TIMESTAMP,"
						"sex CHAR(8) NOT NULL)" };
std::ofstream outfile;

      主函数如下:

int test_libpqxx()
{
#ifdef _MSC_VER
	outfile.open("../../../testdata/output.txt", std::ios::app);
#else
	outfile.open(../../testdata/output.txt", std::ios::app);
#endif
	if (!outfile.is_open()) {
		std::cerr << "Error:fail to open file to write\n";
		return -1;
	}

	// database
	create_database("Info"); // 创建数据库时,数据库中显示的名字为info,而不是Info
	connect_database("info"); // 连接数据库时指定的数据名区分大小写
	//drop_database("info");

	// table
	create_table("info", table_teacher);
	create_table("info", table_student);

	insert_into_teacher("info", 1111, "Tom", "HeBei", "man");
	insert_into_teacher("info", 5555, "Tom", "北京", "男");
	insert_into_teacher("info", 2222, "小张", "天津", "woman");
	insert_into_teacher("info", 16625, "xx", "xx", "女");

	insert_into_student("info", 8, "小王", "HeBei", 32, 88.8, get_current_time(), "男");
	std::this_thread::sleep_for(std::chrono::seconds(2));
	insert_into_student("info", 28, "小何", "深圳", 8, 22.22, get_current_time(), "女");
	insert_into_student("info", 29, "Lucy", "北京", 18, 22.22, get_current_time(), "woman");
	insert_into_student("info", 33, "Tom", "深圳", 28, 22.22, get_current_time(), "woman");
	insert_into_student("info", 18, "小李", "上海", 28, 66.6, get_current_time(), "女");
	std::this_thread::sleep_for(std::chrono::seconds(2));
	insert_into_student("info", 48, "Tom", "深圳", 38, 22.22, get_current_time(), "woman");

	alter_table("info");
	delete_table_data("info");
	update_table_data("info");
	select_table_data("info");
	drop_table("info", "teacher");

	outfile.close();
	std::cout << "test finish\n";
	return 0;
}

     

      (1).创建数据库时,数据库中显示的名字为小写;连接数据库时指定的数据名区分大小写。

      (2).有中文时需设置client_encoding为GBK。

      (3).Debug模式下,使用的也是PostgreSQL中的release库。

      数据库中student表内容如下:

      执行结果如下:

      GitHubhttps://github.com/fengbingchun/Messy_Test

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值