Skip to content

Commit 355d23f

Browse files
committed
db(core,pool,sqlite,examples): stabilize public API, make pool/tx movable, add SQLite skeleton and examples
- Simplify Statement::bind public API (int-safe overloads) - Make PooledConn movable to support Transaction move semantics - Harden ConnectionPool lifecycle (RAII-safe release) - Prepare SQLite driver structure (headers + src) - Extend Database config handling for multiple engines - Add examples/ with prepared queries and migrations usage - Keep design explicit, SQL-first, anti-ORM by construction
1 parent f9befe6 commit 355d23f

File tree

15 files changed

+697
-56
lines changed

15 files changed

+697
-56
lines changed

CMakeLists.txt

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -34,6 +34,7 @@ option(VIX_DB_REQUIRE_POSTGRES "Fail if Postgres requested but not found" OFF
3434
# Redis is not SQL, but useful for caching / sessions / queue (future)
3535
option(VIX_DB_USE_REDIS "Enable Redis client (future)" OFF)
3636
option(VIX_DB_REQUIRE_REDIS "Fail if Redis requested but not found" OFF)
37+
option(VIX_BUILD_EXAMPLES "Build examples for Vix modules" OFF)
3738

3839
# ------------------------------------------------------------------------------
3940
# Language / PIC
@@ -364,6 +365,13 @@ if (VIX_DB_HAS_REDIS)
364365
endif()
365366
endif()
366367

368+
# ------------------------------------------------------------------------------
369+
# Examples
370+
# ------------------------------------------------------------------------------
371+
if (VIX_DB_BUILD_EXAMPLES)
372+
add_subdirectory(examples)
373+
endif()
374+
367375
# ------------------------------------------------------------------------------
368376
# Install / export via umbrella export-set "VixTargets"
369377
# ------------------------------------------------------------------------------

examples/CMakeLists.txt

Lines changed: 17 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,17 @@
1+
option(VIX_DB_BUILD_EXAMPLES "Build Vix DB examples" OFF)
2+
3+
if (VIX_DB_BUILD_EXAMPLES)
4+
function(vix_db_example name)
5+
add_executable(vix_db_example_${name} ${name}.cpp)
6+
target_link_libraries(vix_db_example_${name}
7+
PRIVATE
8+
vix::db
9+
)
10+
target_compile_features(vix_db_example_${name} PRIVATE cxx_std_20)
11+
endfunction()
12+
13+
vix_db_example(basic_connect)
14+
vix_db_example(prepared_query)
15+
vix_db_example(transaction)
16+
vix_db_example(migrations)
17+
endif()

examples/README.md

Whitespace-only changes.

examples/basic_connect.cpp

Lines changed: 26 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,26 @@
1+
#include <vix/db/db.hpp>
2+
#include <iostream>
3+
4+
using namespace vix::db;
5+
6+
int main()
7+
{
8+
DbConfig cfg;
9+
cfg.engine = Engine::MySQL;
10+
cfg.mysql.host = "tcp://127.0.0.1:3306";
11+
cfg.mysql.user = "root";
12+
cfg.mysql.password = "";
13+
cfg.mysql.database = "vixdb";
14+
15+
Database db(cfg);
16+
17+
auto conn = db.pool().acquire();
18+
if (!conn->ping())
19+
{
20+
std::cerr << "DB ping failed\n";
21+
return 1;
22+
}
23+
24+
std::cout << "DB connected successfully\n";
25+
return 0;
26+
}

examples/migrations.cpp

Lines changed: 100 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,100 @@
1+
#include <vix/db/db.hpp>
2+
3+
#include <iostream>
4+
#include <filesystem>
5+
#include <string>
6+
7+
using namespace vix::db;
8+
9+
static DbConfig make_mysql_cfg()
10+
{
11+
DbConfig cfg;
12+
cfg.engine = Engine::MySQL;
13+
cfg.mysql.host = "tcp://127.0.0.1:3306";
14+
cfg.mysql.user = "root";
15+
cfg.mysql.password = "";
16+
cfg.mysql.database = "vixdb";
17+
cfg.mysql.pool.min = 1;
18+
cfg.mysql.pool.max = 4;
19+
return cfg;
20+
}
21+
22+
// ------------------------------
23+
// 1) Code-based migration example
24+
// ------------------------------
25+
class CreateUsersTable final : public Migration
26+
{
27+
public:
28+
std::string id() const override { return "2026-01-22-create-users"; }
29+
30+
void up(Connection &c) override
31+
{
32+
auto st = c.prepare(
33+
"CREATE TABLE IF NOT EXISTS users ("
34+
" id BIGINT PRIMARY KEY AUTO_INCREMENT,"
35+
" name VARCHAR(255) NOT NULL,"
36+
" age INT NOT NULL"
37+
");");
38+
st->exec();
39+
}
40+
41+
void down(Connection &c) override
42+
{
43+
auto st = c.prepare("DROP TABLE IF EXISTS users;");
44+
st->exec();
45+
}
46+
};
47+
48+
static void run_code_migrations(Database &db)
49+
{
50+
std::cout << "[migrations] running code migrations...\n";
51+
52+
Transaction tx(db.pool());
53+
54+
CreateUsersTable m1;
55+
MigrationsRunner runner(tx.conn());
56+
runner.add(&m1);
57+
runner.runAll();
58+
59+
tx.commit();
60+
std::cout << "[migrations] done (code)\n";
61+
}
62+
63+
// ------------------------------
64+
// 2) File-based migration example
65+
// ------------------------------
66+
static void run_file_migrations(Database &db, std::filesystem::path dir)
67+
{
68+
std::cout << "[migrations] running file migrations from: " << dir.string() << "\n";
69+
70+
Transaction tx(db.pool());
71+
72+
FileMigrationsRunner runner(tx.conn(), std::move(dir));
73+
runner.setTable("schema_migrations"); // optional (default already)
74+
runner.applyAll();
75+
76+
tx.commit();
77+
std::cout << "[migrations] done (files)\n";
78+
}
79+
80+
int main()
81+
{
82+
try
83+
{
84+
Database db(make_mysql_cfg());
85+
86+
// 1) Code migrations
87+
run_code_migrations(db);
88+
89+
// 2) File migrations (expects ./migrations/*.up.sql and *.down.sql)
90+
run_file_migrations(db, std::filesystem::path{"migrations"});
91+
92+
std::cout << "OK\n";
93+
return 0;
94+
}
95+
catch (const std::exception &e)
96+
{
97+
std::cerr << "ERROR: " << e.what() << "\n";
98+
return 1;
99+
}
100+
}

examples/prepared_query.cpp

Lines changed: 32 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,32 @@
1+
#include <vix/db/db.hpp>
2+
#include <iostream>
3+
4+
using namespace vix::db;
5+
6+
int main()
7+
{
8+
DbConfig cfg;
9+
cfg.engine = Engine::MySQL;
10+
cfg.mysql.host = "tcp://127.0.0.1:3306";
11+
cfg.mysql.user = "root";
12+
cfg.mysql.password = "";
13+
cfg.mysql.database = "vixdb";
14+
cfg.mysql.pool.min = 1;
15+
cfg.mysql.pool.max = 8;
16+
17+
Database db(cfg);
18+
19+
auto conn = db.pool().acquire();
20+
auto st = conn->prepare("SELECT id, name FROM users WHERE age > ?");
21+
22+
st->bind(1, 18);
23+
// or: st->bind(1, std::int64_t{18});
24+
// or: st->bind(1, i64(18));
25+
26+
auto rs = st->query();
27+
while (rs->next())
28+
{
29+
const auto &row = rs->row();
30+
std::cout << row.getInt64(0) << " " << row.getString(1) << "\n";
31+
}
32+
}

examples/transaction.cpp

Lines changed: 66 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,66 @@
1+
#include <vix/db/db.hpp>
2+
#include <iostream>
3+
4+
int main()
5+
{
6+
vix::db::DbConfig cfg;
7+
cfg.engine = vix::db::Engine::MySQL;
8+
cfg.mysql.host = "tcp://127.0.0.1:3306";
9+
cfg.mysql.user = "root";
10+
cfg.mysql.password = "";
11+
cfg.mysql.database = "vixdb";
12+
cfg.mysql.pool.min = 1;
13+
cfg.mysql.pool.max = 8;
14+
15+
vix::db::Database db(cfg);
16+
17+
try
18+
{
19+
// 2) Transaction RAII
20+
vix::db::Transaction tx(db.pool());
21+
22+
// Create table (demo)
23+
tx.conn().prepare(
24+
"CREATE TABLE IF NOT EXISTS users ("
25+
" id BIGINT PRIMARY KEY AUTO_INCREMENT,"
26+
" name VARCHAR(255) NOT NULL,"
27+
" age INT NOT NULL"
28+
")")
29+
->exec();
30+
31+
// Insert 1 row
32+
{
33+
auto st = tx.conn().prepare("INSERT INTO users (name, age) VALUES (?, ?)");
34+
st->bind(1, std::string("Alice"));
35+
st->bind(2, static_cast<std::int64_t>(20)); // int -> i64
36+
st->exec();
37+
}
38+
39+
// Query
40+
{
41+
auto st = tx.conn().prepare("SELECT id, name, age FROM users WHERE age >= ?");
42+
st->bind(1, static_cast<std::int64_t>(18));
43+
auto rs = st->query();
44+
45+
while (rs->next())
46+
{
47+
const auto &row = rs->row();
48+
std::cout
49+
<< row.getInt64(0) << " "
50+
<< row.getString(1) << " "
51+
<< row.getInt64(2) << "\n";
52+
}
53+
}
54+
55+
// 3) Commit
56+
tx.commit();
57+
std::cout << "Committed.\n";
58+
}
59+
catch (const std::exception &e)
60+
{
61+
std::cerr << "DB error: " << e.what() << "\n";
62+
return 1;
63+
}
64+
65+
return 0;
66+
}

include/vix/db/Database.hpp

Lines changed: 10 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -31,17 +31,17 @@ namespace vix::db
3131

3232
struct MySQLConfig
3333
{
34-
std::string host; // ex: "tcp://127.0.0.1:3306"
35-
std::string user; // ex: "root"
36-
std::string password; // ex: ""
37-
std::string database; // ex: "vixdb"
38-
PoolConfig pool{}; // min/max connections
34+
std::string host;
35+
std::string user;
36+
std::string password;
37+
std::string database;
38+
PoolConfig pool{};
3939
};
4040

4141
struct SQLiteConfig
4242
{
43-
std::string path; // ex: "vix_orm.db"
44-
PoolConfig pool{}; // reuse PoolConfig
43+
std::string path;
44+
PoolConfig pool{};
4545
};
4646

4747
struct DbConfig
@@ -51,24 +51,23 @@ namespace vix::db
5151
SQLiteConfig sqlite{};
5252
};
5353

54-
ConnectionFactory make_mysql_factory(const MySQLConfig &cfg);
55-
5654
DbConfig make_db_config_from_vix_config(const vix::config::Config &cfg);
5755

5856
class Database
5957
{
6058
public:
6159
explicit Database(const DbConfig &cfg);
60+
6261
Engine engine() const noexcept { return cfg_.engine; }
6362
const DbConfig &config() const noexcept { return cfg_; }
63+
6464
ConnectionPool &pool() noexcept { return pool_; }
6565
const ConnectionPool &pool() const noexcept { return pool_; }
6666

6767
private:
6868
DbConfig cfg_;
6969
ConnectionPool pool_;
7070
};
71-
7271
} // namespace vix::db
7372

74-
#endif // VIX_DB_DATABASE_HPP
73+
#endif

include/vix/db/Transaction.hpp

Lines changed: 22 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -29,18 +29,30 @@ namespace vix::db
2929
pooled_.get().begin();
3030
}
3131

32-
~Transaction()
32+
~Transaction() noexcept
3333
{
34-
if (active_)
35-
try
36-
{
37-
pooled_.get().rollback();
38-
}
39-
catch (...)
40-
{
41-
}
34+
if (!active_)
35+
return;
36+
try
37+
{
38+
pooled_.get().rollback();
39+
}
40+
catch (...)
41+
{
42+
}
4243
}
4344

45+
Transaction(const Transaction &) = delete;
46+
Transaction &operator=(const Transaction &) = delete;
47+
48+
Transaction(Transaction &&other) noexcept
49+
: pooled_(std::move(other.pooled_)), active_(other.active_)
50+
{
51+
other.active_ = false;
52+
}
53+
54+
Transaction &operator=(Transaction &&) = delete;
55+
4456
void commit()
4557
{
4658
pooled_.get().commit();
@@ -55,6 +67,7 @@ namespace vix::db
5567

5668
Connection &conn() { return pooled_.get(); }
5769
};
70+
5871
} // namespace vix::db
5972

6073
#endif // VIX_DB_TRANSACTION_HPP

include/vix/db/core/Drivers.hpp

Lines changed: 7 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -28,12 +28,18 @@ namespace vix::db
2828
{
2929
virtual ~Statement() = default;
3030
virtual void bind(std::size_t idx, const DbValue &v) = 0;
31+
32+
void bind(std::size_t idx, int v) { bind(idx, static_cast<std::int64_t>(v)); }
33+
void bind(std::size_t idx, unsigned v) { bind(idx, static_cast<std::int64_t>(v)); }
3134
void bind(std::size_t idx, std::int64_t v) { bind(idx, i64(v)); }
35+
void bind(std::size_t idx, std::uint64_t v) { bind(idx, static_cast<std::int64_t>(v)); }
3236
void bind(std::size_t idx, double v) { bind(idx, f64(v)); }
3337
void bind(std::size_t idx, bool v) { bind(idx, b(v)); }
3438
void bind(std::size_t idx, std::string v) { bind(idx, str(std::move(v))); }
35-
void bind(std::size_t idx, std::string_view v) { bind(idx, std::string(v)); }
39+
void bind(std::size_t idx, const char *v) { bind(idx, str(std::string(v ? v : ""))); }
40+
3641
void bindNull(std::size_t idx) { bind(idx, null()); }
42+
3743
virtual std::unique_ptr<ResultSet> query() = 0;
3844
virtual std::uint64_t exec() = 0;
3945
};

0 commit comments

Comments
 (0)