Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
7 changes: 7 additions & 0 deletions .github/workflows/db-strict-ci.yml
Original file line number Diff line number Diff line change
Expand Up @@ -14,6 +14,7 @@ on:
- "include/**"
- "src/**"
- "examples/**"
- "tests/**"
- "tools/**"
- "README.md"
- "LICENSE"
Expand All @@ -32,6 +33,7 @@ on:
- "include/**"
- "src/**"
- "examples/**"
- "tests/**"
- "tools/**"
- "README.md"
- "LICENSE"
Expand Down Expand Up @@ -189,6 +191,7 @@ jobs:
-DCMAKE_BUILD_TYPE=Debug \
-DCMAKE_EXPORT_COMPILE_COMMANDS=ON \
-DVIX_ENABLE_SANITIZERS=OFF \
-DVIX_DB_BUILD_TESTS=ON \
-DVIX_DB_BUILD_EXAMPLES=${{ matrix.examples }} \
-DVIX_DB_USE_MYSQL=${{ matrix.mysql }} \
-DVIX_DB_REQUIRE_MYSQL=OFF \
Expand All @@ -205,6 +208,10 @@ jobs:
run: |
cmake --build build -j"${BUILD_JOBS}"

- name: Test
run: |
ctest --test-dir build --output-on-failure

- name: Print executables
run: |
find build -type f -executable | sort || true
Expand Down
34 changes: 34 additions & 0 deletions CMakeLists.txt
Original file line number Diff line number Diff line change
Expand Up @@ -224,6 +224,8 @@ set(VIX_DB_PUBLIC_HEADERS
include/vix/db/mig/Migration.hpp
include/vix/db/mig/MigrationsRunner.hpp
include/vix/db/mig/FileMigrationsRunner.hpp
include/vix/db/mig/sql/MySqlGenerator.hpp
include/vix/db/mig/sql/SQLiteGenerator.hpp
)

set(VIX_DB_SOURCES
Expand All @@ -235,6 +237,7 @@ set(VIX_DB_SOURCES
src/schema/Json.cpp
src/mig/diff/Diff.cpp
src/mig/sql/MySqlGenerator.cpp
src/mig/sql/SQLiteGenerator.cpp
)

# MySQL driver sources
Expand Down Expand Up @@ -426,6 +429,37 @@ if (VIX_DB_BUILD_EXAMPLES)
add_subdirectory(examples)
endif()

# ------------------------------------------------------------------------------
# Tests
# ------------------------------------------------------------------------------
if (VIX_DB_BUILD_TESTS)
enable_testing()

if (VIX_DB_HAS_SQLITE)
add_executable(vix_db_sql_generator_tests
tests/sql_generator_tests.cpp
tools/migrator/MakeMigrations.cpp
)

target_link_libraries(vix_db_sql_generator_tests PRIVATE vix::db)
target_compile_features(vix_db_sql_generator_tests PRIVATE cxx_std_20)

if (MSVC)
target_compile_options(vix_db_sql_generator_tests PRIVATE ${_WARNINGS_MSVC})
else()
target_compile_options(vix_db_sql_generator_tests PRIVATE ${_WARNINGS_GNU})
endif()

if (COMMAND vix_enable_sanitizers)
vix_enable_sanitizers(vix_db_sql_generator_tests)
endif()

add_test(NAME vix_db_sql_generator_tests COMMAND vix_db_sql_generator_tests)
else()
message(STATUS "[vix_db] tests requested but SQLite is disabled; SQL execution tests skipped.")
endif()
endif()

# ------------------------------------------------------------------------------
# Install / export via umbrella export-set "VixTargets"
# ------------------------------------------------------------------------------
Expand Down
10 changes: 10 additions & 0 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -198,6 +198,16 @@ vix build --clean
vix build --preset release
```

## Migration SQL generation

Generate file-based migrations from a schema snapshot with either MySQL or SQLite SQL:

```bash
vix_db_migrator makemigrations --new ./schema.new.json --snapshot ./schema.json --dir ./migrations --name create_users --dialect sqlite
```

SQLite migration generation emits native DDL for table, column, and index operations. Unsupported SQLite `ADD COLUMN` forms, such as adding primary-key, unique, autoincrement, or required columns without defaults, fail before writing migration files. Down migrations for dropped columns use SQLite 3.35+ `DROP COLUMN` syntax.

## Tests

Build all targets first, then run tests:
Expand Down
50 changes: 50 additions & 0 deletions include/vix/db/mig/sql/SQLiteGenerator.hpp
Original file line number Diff line number Diff line change
@@ -0,0 +1,50 @@
/**
*
* @file SQLiteGenerator.hpp
* @author Gaspard Kirira
*
* Copyright 2025, Gaspard Kirira.
* All rights reserved.
* https://github.com/vixcpp/vix
*
* Use of this source code is governed by a MIT license
* that can be found in the License file.
*
* Vix.cpp
*/
#ifndef VIX_DB_MIG_SQL_SQLITE_GENERATOR_HPP
#define VIX_DB_MIG_SQL_SQLITE_GENERATOR_HPP

#include <vix/db/mig/diff/Op.hpp>

#include <string>
#include <vector>

namespace vix::db::mig::sql
{
/**
* @brief Generate SQLite SQL statements for applying a migration.
*
* Converts portable migration operations into SQLite DDL. Unsupported
* SQLite alterations fail with std::runtime_error before any SQL script
* is returned.
*
* @param ops Ordered list of migration operations.
* @return SQL script for the "up" migration.
*/
std::string to_sqlite_up(const std::vector<vix::db::mig::diff::Op> &ops);

/**
* @brief Generate SQLite SQL statements for reverting a migration.
*
* Produces the inverse SQL script in reverse operation order.
* SQLite DROP COLUMN is emitted using the native SQLite 3.35+ syntax.
*
* @param ops Ordered list of migration operations.
* @return SQL script for the "down" migration.
*/
std::string to_sqlite_down(const std::vector<vix::db::mig::diff::Op> &ops);

} // namespace vix::db::mig::sql

#endif // VIX_DB_MIG_SQL_SQLITE_GENERATOR_HPP
21 changes: 11 additions & 10 deletions src/mig/diff/Diff.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -24,25 +24,26 @@ namespace vix::db::mig::diff
auto A = map_tables(from);
auto B = map_tables(to);

// 1) Drop tables missing in 'to'
for (const auto &[name, ta] : A)
// 1) Drop tables missing in 'to', preserving source schema order.
for (const auto &table : from.tables)
{
if (!B.count(name))
ops.push_back(DropTable{*ta});
if (!B.count(table.name))
ops.push_back(DropTable{table});
}

// 2) Create tables new in 'to'
for (const auto &[name, tb] : B)
// 2) Create tables new in 'to', preserving target schema order.
for (const auto &targetTable : to.tables)
{
const auto &name = targetTable.name;
if (!A.count(name))
{
ops.push_back(CreateTable{*tb});
ops.push_back(CreateTable{targetTable});
continue;
}

// 3) Same table: diff columns + indexes
const auto *oldT = A.at(name);
const auto *newT = tb;
const auto *newT = &targetTable;

// Columns: drops
for (const auto &c_old : oldT->columns)
Expand All @@ -52,7 +53,7 @@ namespace vix::db::mig::diff
}

// Columns: adds
for (const auto &c_new : newT->columns)
for (const auto &c_new : targetTable.columns)
{
if (!oldT->findColumn(c_new.name))
ops.push_back(AddColumn{name, c_new});
Expand All @@ -66,7 +67,7 @@ namespace vix::db::mig::diff
}

// Indexes: adds
for (const auto &i_new : newT->indexes)
for (const auto &i_new : targetTable.indexes)
{
if (!oldT->findIndex(i_new.name))
ops.push_back(CreateIndex{name, i_new});
Expand Down
Loading