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/bashif [ $# != 2 ]; thenecho "Error: requires two parameters: 1: dynamic or static; 2: Relese or Debug"echo "For example: dynamic library, debug mode: $0 dynamic Debug"exit -1
fiif [ $1 != "dynamic" ] && [ $1 != "static" ]; thenecho "Error: the first parameter can only be dynamic or static"exit -1
fiif [ $2 != "Release" ] && [ $2 != "Debug" ]; thenecho "Error: the second parameter can only be Release or Debug"exit -1
fiif [[ ! -d "build" ]]; thenmkdir buildcd build
elsecd build
fidynamic_flag=ON
if [ $1 != "dynamic" ]; thendynamic_flag=OFF
fiecho "build: $1 $2"# vs2022, PostgreSQL 16.4cmake \-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 existc.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:UTF8if (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_VERoutfile.open("../../../testdata/output.txt", std::ios::app);
#elseoutfile.open(../../testdata/output.txt", std::ios::app);
#endifif (!outfile.is_open()) {std::cerr << "Error:fail to open file to write\n";return -1;}// databasecreate_database("Info"); // 创建数据库时,数据库中显示的名字为info,而不是Infoconnect_database("info"); // 连接数据库时指定的数据名区分大小写//drop_database("info");// tablecreate_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表内容如下:
执行结果如下:
GitHub:https://github.com/fengbingchun/Messy_Test