C++ и PostgreSQL: как подружить системное программирование и реляционные базы данных
Вы когда-нибудь задумывались, почему многие высоконагруженные системы — от игровых серверов до финансовых платформ — выбирают связку C++ и PostgreSQL? Ответ прост: C++ даёт молниеносную скорость и контроль над памятью, а PostgreSQL — надёжность и мощные SQL-возможности. Это не просто «ещё один туториал». Это практическое руководство, которое проведёт вас от установки драйвера до написания полноценного CRUD-приложения на C++ с PostgreSQL.
В этой статье вы узнаете:
- Как установить и настроить libpq — официальный клиент PostgreSQL для C++.
- Как выполнять SELECT, INSERT, UPDATE и DELETE запросы.
- Как обрабатывать ошибки и работать с транзакциями.
- Примеры реального кода, который можно сразу использовать в своих проектах.
Готовы? Поехали!
Шаг 1. Установка libpq и подготовка окружения
Прежде чем писать код, нужно установить библиотеку libpq. Она входит в состав клиентской части PostgreSQL. Если у вас уже установлен PostgreSQL, libpq уже есть. Если нет — установите её отдельно.
Linux (Ubuntu/Debian)
sudo apt update
sudo apt install libpq-dev postgresql-server-dev-all
macOS (Homebrew)
brew install postgresql
Windows
Скачайте установщик с официального сайта PostgreSQL и при установке выберите компонент «Development Libraries».
После установки проверьте, что компилятор g++ может найти библиотеку. Для этого при компиляции нужно будет указать флаги -lpq (линковка с libpq) и, возможно, -I/путь/к/include.
Шаг 2. Подключение к базе данных на C++
Начнём с самого главного — установления соединения. Для этого используется структура PGconn и функция PQconnectdb().
Пример: простое подключение
#include <iostream>
#include <libpq-fe.h>
int main() {
// Параметры подключения
const char* conninfo = "host=localhost port=5432 dbname=mydb user=myuser password=mypass";
// Устанавливаем соединение
PGconn* conn = PQconnectdb(conninfo);
// Проверяем статус
if (PQstatus(conn) != CONNECTION_OK) {
std::cerr << "Ошибка подключения: " << PQerrorMessage(conn) << std::endl;
PQfinish(conn);
return 1;
}
std::cout << "Подключение к PostgreSQL успешно!" << std::endl;
// Закрываем соединение
PQfinish(conn);
return 0;
}
Важно: никогда не хардкодьте пароли в коде. Используйте переменные окружения или файлы конфигурации. В примере выше показано для наглядности.
Скомпилируем:
g++ -o connect connect.cpp -lpq
Шаг 3. Выполнение SQL-запросов: SELECT, INSERT, UPDATE, DELETE
Теперь, когда соединение установлено, научимся выполнять запросы. Для этого используется функция PQexec(), которая возвращает результат в виде структуры PGresult.
Пример: выполнение SELECT
PGresult* res = PQexec(conn, "SELECT id, name, age FROM users");
if (PQresultStatus(res) != PGRES_TUPLES_OK) {
std::cerr << "Ошибка запроса: " << PQerrorMessage(conn) << std::endl;
PQclear(res);
PQfinish(conn);
return 1;
}
// Получаем количество строк и столбцов
int rows = PQntuples(res);
int cols = PQnfields(res);
// Выводим заголовки столбцов
for (int i = 0; i < cols; ++i) {
std::cout << PQfname(res, i) << "\t";
}
std::cout << std::endl;
// Выводим данные
for (int i = 0; i < rows; ++i) {
for (int j = 0; j < cols; ++j) {
std::cout << PQgetvalue(res, i, j) << "\t";
}
std::cout << std::endl;
}
PQclear(res);
Пример: INSERT с параметрами (безопасно)
Никогда не конкатенируйте строки для SQL! Используйте параметризованные запросы через PQexecParams().
const char* paramValues[2] = {"Alice", "30"};
int paramLengths[2] = {5, 2}; // длины строк
int paramFormats[2] = {0, 0}; // текстовый формат
PGresult* res = PQexecParams(conn,
"INSERT INTO users (name, age) VALUES ($1, $2)",
2, // количество параметров
nullptr, // типы параметров (NULL = автоопределение)
paramValues,
paramLengths,
paramFormats,
0); // текстовый формат результата
if (PQresultStatus(res) != PGRES_COMMAND_OK) {
std::cerr << "Ошибка INSERT: " << PQerrorMessage(conn) << std::endl;
} else {
std::cout << "Запись добавлена!" << std::endl;
}
PQclear(res);
Почему это важно? Параметризованные запросы защищают от SQL-инъекций. Даже если пользователь введёт '; DROP TABLE users; --, база данных воспримет это как строку, а не как команду.
Шаг 4. Обработка ошибок и работа с транзакциями
Ошибки в работе с БД неизбежны. Всегда проверяйте статус результата после каждого запроса. Для транзакций используйте команды BEGIN, COMMIT и ROLLBACK через PQexec().
Пример транзакции
// Начинаем транзакцию
PQexec(conn, "BEGIN");
// Первый запрос
PGresult* res1 = PQexec(conn, "UPDATE accounts SET balance = balance - 100 WHERE id = 1");
if (PQresultStatus(res1) != PGRES_COMMAND_OK) {
PQexec(conn, "ROLLBACK");
std::cerr << "Ошибка: " << PQerrorMessage(conn) << std::endl;
PQclear(res1);
return 1;
}
PQclear(res1);
// Второй запрос
PGresult* res2 = PQexec(conn, "UPDATE accounts SET balance = balance + 100 WHERE id = 2");
if (PQresultStatus(res2) != PGRES_COMMAND_OK) {
PQexec(conn, "ROLLBACK");
std::cerr << "Ошибка: " << PQerrorMessage(conn) << std::endl;
PQclear(res2);
return 1;
}
PQclear(res2);
// Если всё ок — фиксиру