commit 5945cb6a21f6ea0a8049b928d72eb5facd43ef3b
parent c9aac4dfc79e1d117ab056a34a2e196f55a3e4f4
Author: Henry Wilson <henry@henryandlizzy.uk>
Date: Thu, 16 Feb 2023 20:55:23 +0000
sqlite: Add example demonstrating loading and saving to a SQLite database
Diffstat:
3 files changed, 123 insertions(+), 2 deletions(-)
diff --git a/Tupfile b/Tupfile
@@ -4,11 +4,12 @@ CFLAGS = $(COMMON_FLAGS)
CXXFLAGS = -std=c++20 $(COMMON_FLAGS)
LDLIBS_aio = -lrt
+LDLIBS_alsa-simple = -lasound
LDLIBS_gl-asteroids = -lglfw -lGL -lm -lasound
LDLIBS_io_uring = -luring
LDLIBS_pulse-async-client = -lpulse
LDLIBS_pulse-simple-client = -lpulse-simple -lm
-LDLIBS_alsa-simple = -lasound
+LDLIBS_sqlite-saveload = -lsqlite3
: foreach src/*.c |> cc $(CFLAGS) -o %o %f $(LDLIBS_%B) |> bin/%B
: foreach src/*.cpp |> c++ $(CXXFLAGS) -o %o %f $(LDLIBS_%B) |> bin/%B
diff --git a/makefile b/makefile
@@ -20,11 +20,12 @@ clean:
$(RM) -r $(c_targets) $(cpp_targets) bin/
bin/aio: -lrt
+bin/alsa-simple: -lasound
bin/gl-asteroids: -lglfw -lGL -lm -lasound
bin/io_uring: -luring
bin/pulse-async-client: -lpulse
bin/pulse-simple-client: -lpulse-simple -lm
-bin/alsa-simple: -lasound
+bin/sqlite-saveload: -lsqlite3
bin/:
mkdir -p $@
diff --git a/src/sqlite-saveload.cpp b/src/sqlite-saveload.cpp
@@ -0,0 +1,119 @@
+#include <unistd.h>
+#include <sqlite3.h>
+
+#include <iostream>
+#include <iterator>
+#include <memory>
+#include <vector>
+
+auto make_sqlite_stmt(sqlite3_stmt* p) -> std::unique_ptr<sqlite3_stmt, decltype(&sqlite3_finalize)>
+{
+ return {p, sqlite3_finalize};
+}
+
+auto make_sqlite_db(sqlite3* p) -> std::unique_ptr<sqlite3, decltype(&sqlite3_close)>
+{
+ return {p, sqlite3_close};
+}
+
+std::vector<int> nums;
+
+void write_nums(char const* path)
+{
+ sqlite3 *db;
+ auto rc = sqlite3_open(path, &db);
+ auto guard_db = make_sqlite_db(db);
+
+ if (rc)
+ throw std::runtime_error("Can't open database for write");
+
+ char* errmsg;
+ char const query[] =
+ "PRAGMA synchronous = OFF;"
+ "PRAGMA journal_mode = OFF;"
+ "CREATE TABLE nums (n int)";
+
+ if (sqlite3_exec(db, query, NULL, NULL, &errmsg))
+ throw std::runtime_error(errmsg);
+
+ sqlite3_stmt* stmt;
+ rc = sqlite3_prepare_v2(db, "INSERT INTO nums (n) VALUES (?)", -1, &stmt, NULL);
+ auto guard = make_sqlite_stmt(stmt);
+
+ if (rc)
+ throw std::runtime_error("Error preparing statement");
+
+ if (sqlite3_exec(db, "begin transaction", NULL, NULL, &errmsg))
+ throw std::runtime_error(errmsg);
+
+ for (unsigned i = 0; i < nums.size(); ++i)
+ {
+ if (sqlite3_bind_int(stmt, 1, nums[i]))
+ throw std::runtime_error("Failed to bind int");
+
+ if (sqlite3_step(stmt) != SQLITE_DONE)
+ throw std::runtime_error("Statement failed to execute");
+
+ if (sqlite3_reset(stmt))
+ throw std::runtime_error("Statement failed to reset");
+ }
+
+ if (sqlite3_exec(db, "end transaction", NULL, NULL, &errmsg))
+ throw std::runtime_error(errmsg);
+}
+
+void add_nums()
+{
+ std::copy(std::istream_iterator<int>(std::cin),
+ std::istream_iterator<int>(),
+ std::back_inserter(nums));
+}
+
+void read_nums(char const* path)
+{
+ sqlite3 *db;
+ auto rc = sqlite3_open_v2(path, &db, SQLITE_OPEN_READONLY, NULL);
+ auto guard_db = make_sqlite_db(db);
+
+ if (rc)
+ throw std::runtime_error("Can't open database for read");
+
+ sqlite3_stmt* stmt;
+ rc = sqlite3_prepare_v2(db, "SELECT n FROM nums", -1, &stmt, NULL);
+ auto guard = make_sqlite_stmt(stmt);
+
+ if (rc)
+ throw std::runtime_error("Error preparing statement");
+
+ while (sqlite3_step(stmt) == SQLITE_ROW)
+ nums.push_back(sqlite3_column_int(stmt, 0));
+
+ std::cout << "Read " << nums.size() << " numbers\n";
+}
+
+int main(int argc, char **argv)
+{
+ switch (argc)
+ {
+ case 3:
+ read_nums(argv[1]);
+ add_nums();
+ write_nums(argv[2]);
+ break;
+ case 2:
+ if (not access(argv[1], F_OK))
+ {
+ read_nums(argv[1]);
+ add_nums();
+ truncate(argv[1], 0);
+ }
+ else
+ add_nums();
+
+ write_nums(argv[1]);
+ break;
+ default:
+ std::cerr << "Usage: " << argv[0] << " ( in out ) | inout\n";
+ return 1;
+ }
+}