Транзакция SQLite в C++
По умолчанию SQLite фиксирует каждый запрос отдельно.
Команды BEGIN, COMMIT и ROLLBACK
через sqlite3_exec складывают шаги в одну
транзакцию до явной фиксации или отката.
Два успешных INSERT попадут на диск только
после COMMIT:
#include <iostream>
#include <sqlite3.h>
int main() {
sqlite3* db = nullptr;
if (sqlite3_open("txn_ok.db", &db) != SQLITE_OK) {
std::cerr << sqlite3_errmsg(db) << "\n";
sqlite3_close(db);
return 1;
}
char* err = nullptr;
if (sqlite3_exec(
db,
"CREATE TABLE IF NOT EXISTS moves ("
"id INTEGER PRIMARY KEY,"
"step TEXT NOT NULL"
")",
nullptr,
nullptr,
&err
) != SQLITE_OK) {
std::cerr << err << "\n";
sqlite3_free(err);
sqlite3_close(db);
return 1;
}
if (sqlite3_exec(db, "BEGIN", nullptr, nullptr, &err)
!= SQLITE_OK) {
std::cerr << err << "\n";
sqlite3_free(err);
sqlite3_close(db);
return 1;
}
sqlite3_stmt* first = nullptr;
if (sqlite3_prepare_v2(
db,
"INSERT INTO moves (step) VALUES (?)",
-1,
&first,
nullptr
) != SQLITE_OK) {
std::cerr << sqlite3_errmsg(db) << "\n";
sqlite3_close(db);
return 1;
}
sqlite3_bind_text(first, 1, "load", -1, SQLITE_STATIC);
sqlite3_step(first);
sqlite3_finalize(first);
sqlite3_stmt* second = nullptr;
if (sqlite3_prepare_v2(
db,
"INSERT INTO moves (step) VALUES (?)",
-1,
&second,
nullptr
) != SQLITE_OK) {
std::cerr << sqlite3_errmsg(db) << "\n";
sqlite3_close(db);
return 1;
}
sqlite3_bind_text(second, 1, "seal", -1, SQLITE_STATIC);
sqlite3_step(second);
sqlite3_finalize(second);
if (sqlite3_exec(db, "COMMIT", nullptr, nullptr, &err)
!= SQLITE_OK) {
std::cerr << err << "\n";
sqlite3_free(err);
sqlite3_close(db);
return 1;
}
sqlite3_close(db);
return 0;
}
При ошибке между шагами ROLLBACK отменяет
несохраненные изменения в рамках транзакции:
#include <iostream>
#include <sqlite3.h>
int main() {
sqlite3* db = nullptr;
if (sqlite3_open("txn_fail.db", &db) != SQLITE_OK) {
std::cerr << sqlite3_errmsg(db) << "\n";
sqlite3_close(db);
return 1;
}
char* err = nullptr;
if (sqlite3_exec(
db,
"CREATE TABLE IF NOT EXISTS moves ("
"id INTEGER PRIMARY KEY,"
"step TEXT NOT NULL"
")",
nullptr,
nullptr,
&err
) != SQLITE_OK) {
std::cerr << err << "\n";
sqlite3_free(err);
sqlite3_close(db);
return 1;
}
if (sqlite3_exec(db, "BEGIN", nullptr, nullptr, &err)
!= SQLITE_OK) {
std::cerr << err << "\n";
sqlite3_free(err);
sqlite3_close(db);
return 1;
}
sqlite3_stmt* insert = nullptr;
if (sqlite3_prepare_v2(
db,
"INSERT INTO moves (step) VALUES (?)",
-1,
&insert,
nullptr
) != SQLITE_OK) {
std::cerr << sqlite3_errmsg(db) << "\n";
sqlite3_close(db);
return 1;
}
sqlite3_bind_text(insert, 1, "start", -1, SQLITE_STATIC);
sqlite3_step(insert);
sqlite3_finalize(insert);
sqlite3_exec(db, "ROLLBACK", nullptr, nullptr, &err);
sqlite3_stmt* count = nullptr;
if (sqlite3_prepare_v2(
db,
"SELECT COUNT(*) FROM moves",
-1,
&count,
nullptr
) != SQLITE_OK) {
std::cerr << sqlite3_errmsg(db) << "\n";
sqlite3_close(db);
return 1;
}
if (sqlite3_step(count) == SQLITE_ROW) {
std::cout << sqlite3_column_int(count, 0) << "\n";
}
sqlite3_finalize(count);
sqlite3_close(db);
return 0;
}
Создайте таблицу ledger_tx в bank.db
с полем note, выполните два добавления
в одной транзакции и зафиксируйте их.
Повторите два добавления, но вызовите откат перед
фиксацией и выведите 0 строк в таблице.