mirror of
https://github.com/latentPrion/cppbessot.git
synced 2026-06-24 15:48:37 +00:00
Compare commits
19 Commits
f507cda960
..
master
| Author | SHA1 | Date | |
|---|---|---|---|
| e87c0fe8ec | |||
| f2aa8d5c15 | |||
| 13ce72f9e5 | |||
| 21cdeedc84 | |||
| 0872cb8078 | |||
| f56b6b8989 | |||
| 9487dd4778 | |||
| b656569fc0 | |||
| 6bb451dd94 | |||
| 588f847296 | |||
| 254b7462ba | |||
| c34b959b36 | |||
| 3d26d89742 | |||
| e00601c721 | |||
| e09625c466 | |||
| 68724dff8f | |||
| e12ad0ecac | |||
| b16421fd08 | |||
| 7e6e5f5081 |
@@ -1,3 +1,4 @@
|
||||
# Ignore any directory named `db` or `build` anywhere in this repository.
|
||||
**/build/
|
||||
/b-*/
|
||||
node_modules/
|
||||
|
||||
@@ -0,0 +1,11 @@
|
||||
# Project Instructions
|
||||
|
||||
- Always break functions into logical subfunctions. No long-scrolling functions, in any language. This applies to source code, scripts, build scripts, CMake, Makefiles, and similar project files. Preserve this subfunction splitting discipline during refactors.
|
||||
- Modularity is non-negotiable. Always group logically related functions together into a module. Preserve modularity during refactors.
|
||||
- Reuse or extend existing abstractions instead of duplicating logic wherever possible. Don't repeat yourself. The goal here is to prevent duplication. Not to discourage appropriate logical separation of prior abstractions into new logical abstractions where sensible.
|
||||
- Always isolate configurable behaviour into configuration variables appropriate for the language and framework being used.
|
||||
- Never bake in literals; at minimum, declare them at the top of the file with a semantically meaningful name.
|
||||
- UI should be responsive. Always prefer to use pre-packaged UI toolkit widgets, containers and colour sets harmoniously, instead of writing custom CSS overrides. Write custom CSS only if there's no UI toolkit mechanism available.
|
||||
- Aggressively isolate, split off, deduplicate and reuse code which can be made into common library code. Do the same with UI elements. Do this both when implementing new features and opportunistically while refactoring or changing old code/UI elements.
|
||||
- Names of files, functions, classes, abstractions, database fields, etc should be aimed at disambiguating purpose and function, rather than at brevity.
|
||||
- Whenever a backend invariant changes; or in general whenever a backend feature/change can be automatically tested, add all tests reasonable, erring on the side of complete coverage over incomplete coverage.
|
||||
+19
-4
@@ -9,10 +9,25 @@ if(NOT DEFINED DB_SCHEMA_DIR_TO_GENERATE OR "${DB_SCHEMA_DIR_TO_GENERATE}" STREQ
|
||||
"Set DB_SCHEMA_DIR_TO_GENERATE to the exact schema directory basename to test, for example -DDB_SCHEMA_DIR_TO_GENERATE=v1.2.")
|
||||
endif()
|
||||
|
||||
set(CPPBESSOT_ODB_TEST_SQLITE_CONNSTR "" CACHE STRING
|
||||
"Optional SQLite connection string for ODB runtime tests")
|
||||
set(CPPBESSOT_ODB_TEST_PGSQL_CONNSTR "" CACHE STRING
|
||||
"Optional PostgreSQL conninfo string for ODB runtime tests")
|
||||
include("${CMAKE_CURRENT_SOURCE_DIR}/cmake/dbDependencyCheck.cmake")
|
||||
cppbessot_check_dependencies()
|
||||
|
||||
set(CPPBESSOT_DB_SQLITE_PROD_PATH "" CACHE STRING
|
||||
"Optional SQLite DB path for DB_TARGET=prod")
|
||||
set(CPPBESSOT_DB_SQLITE_DEV_PATH "" CACHE STRING
|
||||
"Optional SQLite DB path for DB_TARGET=dev")
|
||||
set(CPPBESSOT_DB_SQLITE_PRODDEV_PATH "" CACHE STRING
|
||||
"Optional SQLite DB path for DB_TARGET=proddev")
|
||||
set(CPPBESSOT_DB_SQLITE_TESTS_PATH "" CACHE STRING
|
||||
"Optional SQLite DB path for DB_TARGET=tests")
|
||||
set(CPPBESSOT_DB_PGSQL_PROD_CONNSTR "" CACHE STRING
|
||||
"Optional PostgreSQL connstr for DB_TARGET=prod")
|
||||
set(CPPBESSOT_DB_PGSQL_DEV_CONNSTR "" CACHE STRING
|
||||
"Optional PostgreSQL connstr for DB_TARGET=dev")
|
||||
set(CPPBESSOT_DB_PGSQL_PRODDEV_CONNSTR "" CACHE STRING
|
||||
"Optional PostgreSQL connstr for DB_TARGET=proddev")
|
||||
set(CPPBESSOT_DB_PGSQL_TESTS_CONNSTR "" CACHE STRING
|
||||
"Optional PostgreSQL connstr for DB_TARGET=tests")
|
||||
|
||||
include("${CMAKE_CURRENT_SOURCE_DIR}/cmake/CppBeSSOT.cmake")
|
||||
|
||||
|
||||
@@ -1,110 +1,669 @@
|
||||
# CPPBESSOT (C++ BackEnd Single Source of Truth):
|
||||
# cppbessot
|
||||
|
||||
A framework that uses OpenAPI to maintain a single source of truth for the data model of a software project. It generates C++ headers, JSON serdes, ODB-based ORM headers, DB migrations, Typescript types and Zod schemas. I.e: a type-safe backend-to-frontend data model manager.
|
||||
`cppbessot` is a CMake-driven schema pipeline for projects that keep their application data model in OpenAPI and need consistent generated artifacts across C++, TypeScript, Zod, ODB, SQL DDL, and database migrations.
|
||||
|
||||
Basically, it enables one to write a web application whose backend is written in C++. This C++ web application can communicate seamlessly with a Typescript frontend without losing type-safety. We leverage Zod to enforce type safety. So you get type-safety from end to end. From C++ through to the Typescript frontend.
|
||||
The OpenAPI file under a schema directory is the single source of truth. From that, `cppbessot` can:
|
||||
|
||||
It works by specifying the data model in OpenAPI. Then the OpenAPI model is transpiled into both C++ headers (with JSON serdes and ODB ORM for your database of choice) and Typescript types with Zod schema descriptions.
|
||||
- generate C++ model headers and JSON serdes sources
|
||||
- generate ODB ORM sources for SQLite and PostgreSQL
|
||||
- generate SQL DDL snapshots for SQLite and PostgreSQL
|
||||
- generate TypeScript types
|
||||
- generate Zod schemas
|
||||
- generate SQL migration artifacts between two schema versions
|
||||
- build linkable generated C++ libraries
|
||||
- run live DB actions against `dev`, `prod`, `proddev`, or `tests`
|
||||
|
||||
## CMake integration
|
||||
## Repository Layout
|
||||
|
||||
Reusable CMake entry point for embedding in a larger project:
|
||||
The embedded module entry point is:
|
||||
|
||||
- `cmake/CppBeSSOT.cmake`
|
||||
|
||||
The primary schema root folder is configurable via:
|
||||
|
||||
- `CPPBESSOT_WORKDIR` (defaults to `db`)
|
||||
|
||||
This repo also carries self-contained schema fixtures for test runs:
|
||||
The repo also ships test fixtures under:
|
||||
|
||||
- `db/test-schema-v1.1`
|
||||
- `db/test-schema-v1.2`
|
||||
|
||||
## Simple Integration Guide
|
||||
Each schema directory is expected to look roughly like this:
|
||||
|
||||
### 1) Add the module from your parent project
|
||||
```text
|
||||
<CPPBESSOT_WORKDIR>/<schema-dir>/
|
||||
openapi/openapi.yaml
|
||||
generated-cpp-source/
|
||||
generated-odb-source/
|
||||
generated-sql-ddl/
|
||||
generated-ts-types/
|
||||
generated-zod/
|
||||
```
|
||||
|
||||
Migration artifacts live under:
|
||||
|
||||
```text
|
||||
<CPPBESSOT_WORKDIR>/migrations/<from>-<to>/
|
||||
sqlite/
|
||||
postgre/
|
||||
pre-structural-backfill.sh # optional
|
||||
post-structural-backfill.sh # optional
|
||||
```
|
||||
|
||||
## Configure-Time Requirements
|
||||
|
||||
`cppbessot` checks its dependencies during configure.
|
||||
|
||||
Always required:
|
||||
|
||||
- `git`
|
||||
- `java`
|
||||
- `npm`
|
||||
- `npx`
|
||||
- `odb`
|
||||
- `sqlite3` CLI
|
||||
- SQLite development headers and client library
|
||||
- PostgreSQL development headers and client library
|
||||
- `nlohmann_json`
|
||||
- npm packages:
|
||||
- `@openapitools/openapi-generator-cli`
|
||||
- `openapi-zod-client`
|
||||
|
||||
Conditionally required:
|
||||
|
||||
- `psql`
|
||||
- required when PostgreSQL live DB actions are configured
|
||||
- required when real PostgreSQL db-action tests are configured
|
||||
|
||||
## Top-Level CMake Configuration
|
||||
|
||||
When configuring the standalone `cppbessot` repo itself, `DB_SCHEMA_DIR_TO_GENERATE` must be set explicitly:
|
||||
|
||||
```bash
|
||||
cmake -S cmake/cppbessot -B build-cppbessot -DDB_SCHEMA_DIR_TO_GENERATE=test-schema-v1.2
|
||||
```
|
||||
|
||||
The standalone top-level file also exposes these DB target mapping cache variables:
|
||||
|
||||
- `CPPBESSOT_DB_SQLITE_PROD_PATH`
|
||||
- `CPPBESSOT_DB_SQLITE_DEV_PATH`
|
||||
- `CPPBESSOT_DB_SQLITE_PRODDEV_PATH`
|
||||
- `CPPBESSOT_DB_SQLITE_TESTS_PATH`
|
||||
- `CPPBESSOT_DB_PGSQL_PROD_CONNSTR`
|
||||
- `CPPBESSOT_DB_PGSQL_DEV_CONNSTR`
|
||||
- `CPPBESSOT_DB_PGSQL_PRODDEV_CONNSTR`
|
||||
- `CPPBESSOT_DB_PGSQL_TESTS_CONNSTR`
|
||||
|
||||
## Embedding In A Parent Project
|
||||
|
||||
Minimal parent-project integration:
|
||||
|
||||
```cmake
|
||||
# Parent project CMakeLists.txt
|
||||
cmake_minimum_required(VERSION 3.16)
|
||||
project(MyApp LANGUAGES CXX)
|
||||
cmake_minimum_required(VERSION 3.20)
|
||||
project(my_app LANGUAGES CXX)
|
||||
|
||||
# Optional: where schema directories live (default is "db")
|
||||
set(CPPBESSOT_WORKDIR "db")
|
||||
|
||||
# Required: exact schema directory basename under CPPBESSOT_WORKDIR
|
||||
set(DB_SCHEMA_DIR_TO_GENERATE "v1.1")
|
||||
|
||||
# Optional: only needed if you will run db_gen_migrations
|
||||
# set(DB_SCHEMA_DIR_MIGRATION_FROM "v1.1")
|
||||
# set(DB_SCHEMA_DIR_MIGRATION_TO "v1.2")
|
||||
set(DB_SCHEMA_DIR_TO_GENERATE "v1.2")
|
||||
|
||||
include(path/to/cppbessot/cmake/CppBeSSOT.cmake)
|
||||
```
|
||||
|
||||
### 2) Build generation targets manually
|
||||
By default, including `CppBeSSOT.cmake` auto-registers all generation targets, live DB action targets, and generated libraries.
|
||||
|
||||
```bash
|
||||
cmake -S . -B build
|
||||
cmake --build build --target db_gen_orm_serdes_and_zod
|
||||
cmake --build build --target db_gen_sql_ddl
|
||||
cmake --build build --target db_check_schema_changes
|
||||
```
|
||||
|
||||
Optional migration generation:
|
||||
|
||||
```bash
|
||||
cmake -S . -B build \
|
||||
-DDB_SCHEMA_DIR_MIGRATION_FROM=v1.1 \
|
||||
-DDB_SCHEMA_DIR_MIGRATION_TO=v1.2
|
||||
cmake --build build --target db_gen_migrations
|
||||
```
|
||||
|
||||
### 2b) Build the bundled serdes tests
|
||||
|
||||
These tests validate that checked-in generated C++ model code can be compiled and used for JSON round trips. They are owned by `cppbessot`, not by any parent project.
|
||||
|
||||
```bash
|
||||
git submodule update --init --recursive tests/googletest
|
||||
cmake -S . -B build-tests -DBUILD_TESTING=ON -DDB_SCHEMA_DIR_TO_GENERATE=test-schema-v1.2
|
||||
cmake --build build-tests --target cpp_serdes_test_schema_v1_2
|
||||
ctest --test-dir build-tests --output-on-failure
|
||||
```
|
||||
|
||||
The local test fixtures live under `db/test-schema-v1.1` and `db/test-schema-v1.2`. They intentionally differ so migration generation has real additive schema changes to process.
|
||||
|
||||
For ODB runtime tests, also provide backend connection strings:
|
||||
|
||||
```bash
|
||||
cmake -S . -B build-tests \
|
||||
-DBUILD_TESTING=ON \
|
||||
-DDB_SCHEMA_DIR_TO_GENERATE=test-schema-v1.1 \
|
||||
-DCPPBESSOT_ODB_TEST_SQLITE_CONNSTR=/tmp/cppbessot-odb-test.sqlite \
|
||||
-DCPPBESSOT_ODB_TEST_PGSQL_CONNSTR="host=127.0.0.1 port=5432 dbname=cppbessot_test user=postgres password=postgres"
|
||||
cmake --build build-tests --target cpp_odb_orm_sqlite_test_schema_v1_1
|
||||
cmake --build build-tests --target cpp_odb_orm_pgsql_test_schema_v1_1
|
||||
ctest --test-dir build-tests --output-on-failure
|
||||
```
|
||||
|
||||
Use a dedicated PostgreSQL test database. The ODB runtime tests recreate the schema.
|
||||
The ODB runtime tests also verify hydration from ORM query result sets by persisting multiple rows, querying them back through `odb::result<T>`, and asserting that distinct field values are materialized correctly for both SQLite and PostgreSQL.
|
||||
The configured CMake connstring values are baked into the test binaries as defaults, so direct binary execution works without exporting environment variables first. If the matching environment variable is set at runtime, it still overrides the compiled default.
|
||||
|
||||
### 3) Link generated libraries
|
||||
|
||||
```cmake
|
||||
target_link_libraries(my_app PRIVATE
|
||||
cppbessot::openai_model_gen
|
||||
cppbessot::odb_sqlite # if sqlite odb sources exist
|
||||
cppbessot::odb_pgsql # if postgre odb sources exist
|
||||
)
|
||||
```
|
||||
|
||||
### Optional manual enable mode
|
||||
If you want manual control:
|
||||
|
||||
```cmake
|
||||
set(CPPBESSOT_AUTO_ENABLE OFF)
|
||||
include(path/to/cppbessot/cmake/CppBeSSOT.cmake)
|
||||
cppbessot_enable()
|
||||
```
|
||||
|
||||
## CMake Variables
|
||||
|
||||
### Core Schema Variables
|
||||
|
||||
- `CPPBESSOT_WORKDIR`
|
||||
- default: `db`
|
||||
- schema root folder, relative to `PROJECT_SOURCE_DIR` or absolute
|
||||
|
||||
- `DB_SCHEMA_DIR_TO_GENERATE`
|
||||
- default in module mode: `v1.1`
|
||||
- required in standalone top-level configure
|
||||
- basename of the schema directory to generate artifacts for
|
||||
|
||||
- `DB_SCHEMA_DIR_MIGRATION_FROM`
|
||||
- default: empty
|
||||
- source schema basename for `db_gen_migrations`
|
||||
|
||||
- `DB_SCHEMA_DIR_MIGRATION_TO`
|
||||
- default: empty
|
||||
- target schema basename for `db_gen_migrations`
|
||||
|
||||
- `CPPBESSOT_GEN_MIGRATION_BACKENDS`
|
||||
- default: `sqlite;pgsql`
|
||||
- ODB backends for `db_gen_migrations`
|
||||
- supported values: `sqlite`, `pgsql`
|
||||
- `pgsql` writes artifacts under the historical `postgre` output directory
|
||||
- compatibility alias: `CPPBESSOT_GEN_MIGRATIONS_BACKENDS`
|
||||
|
||||
- `DB_SCHEMA_CHANGES_ARE_ERROR`
|
||||
- default: `OFF`
|
||||
- used by schema-drift checking logic
|
||||
|
||||
- `CPPBESSOT_AUTO_ENABLE`
|
||||
- default: `ON`
|
||||
- when `ON`, including `CppBeSSOT.cmake` immediately registers targets and libraries
|
||||
|
||||
### Live DB Action Variables
|
||||
|
||||
- `DB_TARGET`
|
||||
- default: `dev`
|
||||
- selected live DB target for `db_createfrom` and `db_migrate`
|
||||
- allowed values: `prod`, `proddev`, `dev`, `tests`
|
||||
|
||||
- `DB_CREATEFROM_SCHEMA_DIR`
|
||||
- default: `DB_SCHEMA_DIR_TO_GENERATE`
|
||||
- schema basename whose generated SQL DDL should be used by `db_createfrom`
|
||||
|
||||
- `DB_MIGRATE_WITH`
|
||||
- default: empty
|
||||
- migration directory basename under `<CPPBESSOT_WORKDIR>/migrations`
|
||||
- example: `v1.1-v1.2`
|
||||
|
||||
- `DB_MIGRATE_PRODDEV_USE_STALE`
|
||||
- default: `OFF`
|
||||
- when `ON` and `DB_TARGET=proddev`, reuse the existing proddev target instead of recloning from prod
|
||||
|
||||
### SQLite Live DB Mapping Variables
|
||||
|
||||
Exactly one backend mapping must be set for the selected `DB_TARGET`.
|
||||
|
||||
- `CPPBESSOT_DB_SQLITE_PROD_PATH`
|
||||
- `CPPBESSOT_DB_SQLITE_DEV_PATH`
|
||||
- `CPPBESSOT_DB_SQLITE_PRODDEV_PATH`
|
||||
- `CPPBESSOT_DB_SQLITE_TESTS_PATH`
|
||||
|
||||
These point at the SQLite DB file to act on for `prod`, `dev`, `proddev`, or `tests`.
|
||||
|
||||
### PostgreSQL Live DB Mapping Variables
|
||||
|
||||
- `CPPBESSOT_DB_PGSQL_PROD_CONNSTR`
|
||||
- `CPPBESSOT_DB_PGSQL_DEV_CONNSTR`
|
||||
- `CPPBESSOT_DB_PGSQL_PRODDEV_CONNSTR`
|
||||
- `CPPBESSOT_DB_PGSQL_TESTS_CONNSTR`
|
||||
|
||||
These are `psql`-compatible PostgreSQL connection strings for `prod`, `dev`, `proddev`, or `tests`.
|
||||
|
||||
### Prod-To-Proddev Clone Hook Variables
|
||||
|
||||
These are only needed for `DB_TARGET=proddev` when `DB_MIGRATE_PRODDEV_USE_STALE=OFF`.
|
||||
|
||||
- `CPPBESSOT_DB_SQLITE_CLONE_PROD_TO_PRODDEV_COMMAND`
|
||||
- `CPPBESSOT_DB_PGSQL_CLONE_PROD_TO_PRODDEV_COMMAND`
|
||||
|
||||
They must be shell command strings that clone the `prod` database into `proddev` for the chosen backend.
|
||||
|
||||
## Command Targets
|
||||
|
||||
All custom targets are `EXCLUDE_FROM_ALL`, so they run only when explicitly requested or when a dependent library is built.
|
||||
|
||||
### `db_check_schema_changes`
|
||||
|
||||
Purpose:
|
||||
|
||||
- checks for git-tracked schema changes under `CPPBESSOT_WORKDIR`
|
||||
|
||||
Primary variables:
|
||||
|
||||
- `CPPBESSOT_WORKDIR`
|
||||
- `DB_SCHEMA_CHANGES_ARE_ERROR`
|
||||
|
||||
Output:
|
||||
|
||||
- no generated files
|
||||
- emits success/failure diagnostics about dirty schema state
|
||||
|
||||
### `db_gen_ts`
|
||||
|
||||
Purpose:
|
||||
|
||||
- generates TypeScript types from `openapi/openapi.yaml`
|
||||
|
||||
Primary variables:
|
||||
|
||||
- `CPPBESSOT_WORKDIR`
|
||||
- `DB_SCHEMA_DIR_TO_GENERATE`
|
||||
|
||||
Output:
|
||||
|
||||
- `<CPPBESSOT_WORKDIR>/<schema>/generated-ts-types/`
|
||||
|
||||
### `db_gen_zod`
|
||||
|
||||
Purpose:
|
||||
|
||||
- generates Zod schemas from `openapi/openapi.yaml`
|
||||
|
||||
Primary variables:
|
||||
|
||||
- `CPPBESSOT_WORKDIR`
|
||||
- `DB_SCHEMA_DIR_TO_GENERATE`
|
||||
|
||||
Output:
|
||||
|
||||
- `<CPPBESSOT_WORKDIR>/<schema>/generated-zod/schemas.ts`
|
||||
|
||||
### `db_gen_cpp_headers`
|
||||
|
||||
Purpose:
|
||||
|
||||
- generates C++ model headers and JSON serdes/model sources from OpenAPI
|
||||
|
||||
Primary variables:
|
||||
|
||||
- `CPPBESSOT_WORKDIR`
|
||||
- `DB_SCHEMA_DIR_TO_GENERATE`
|
||||
|
||||
Output:
|
||||
|
||||
- `<CPPBESSOT_WORKDIR>/<schema>/generated-cpp-source/include/`
|
||||
- `<CPPBESSOT_WORKDIR>/<schema>/generated-cpp-source/src/`
|
||||
|
||||
### `db_gen_odb_logic`
|
||||
|
||||
Purpose:
|
||||
|
||||
- generates ODB ORM sources for both SQLite and PostgreSQL
|
||||
- depends on `db_gen_cpp_headers`
|
||||
|
||||
Primary variables:
|
||||
|
||||
- `CPPBESSOT_WORKDIR`
|
||||
- `DB_SCHEMA_DIR_TO_GENERATE`
|
||||
|
||||
Output:
|
||||
|
||||
- `<CPPBESSOT_WORKDIR>/<schema>/generated-odb-source/sqlite/`
|
||||
- `<CPPBESSOT_WORKDIR>/<schema>/generated-odb-source/postgre/`
|
||||
|
||||
### `db_gen_sql_ddl`
|
||||
|
||||
Purpose:
|
||||
|
||||
- generates SQL DDL snapshots for both SQLite and PostgreSQL
|
||||
- depends on `db_gen_cpp_headers`
|
||||
|
||||
Primary variables:
|
||||
|
||||
- `CPPBESSOT_WORKDIR`
|
||||
- `DB_SCHEMA_DIR_TO_GENERATE`
|
||||
|
||||
Output:
|
||||
|
||||
- `<CPPBESSOT_WORKDIR>/<schema>/generated-sql-ddl/sqlite/`
|
||||
- `<CPPBESSOT_WORKDIR>/<schema>/generated-sql-ddl/postgre/`
|
||||
|
||||
### `db_gen_migrations`
|
||||
|
||||
Purpose:
|
||||
|
||||
- generates migration SQL artifacts between two schema versions
|
||||
|
||||
Primary variables:
|
||||
|
||||
- `CPPBESSOT_WORKDIR`
|
||||
- `DB_SCHEMA_DIR_MIGRATION_FROM`
|
||||
- `DB_SCHEMA_DIR_MIGRATION_TO`
|
||||
- `CPPBESSOT_GEN_MIGRATION_BACKENDS`
|
||||
|
||||
Output:
|
||||
|
||||
- `<CPPBESSOT_WORKDIR>/migrations/<from>-<to>/sqlite/`
|
||||
- `<CPPBESSOT_WORKDIR>/migrations/<from>-<to>/postgre/`
|
||||
|
||||
Notes:
|
||||
|
||||
- `from` and `to` must differ
|
||||
- if either migration variable is empty, `db_gen_migrations` is still registered but intentionally fails with guidance
|
||||
- enum-only generated C++ headers are skipped; only headers with ODB object pragmas produce migration SQL
|
||||
- missing source-version changelog XML means the target-version model is treated as a new table and generated from an empty changelog baseline
|
||||
- PostgreSQL migrations remain incremental when source-version changelog XML exists
|
||||
- SQLite migrations remain incremental except when ODB reports SQLite's drop-column limitation; in that case only that model is regenerated from the empty changelog baseline with a warning
|
||||
- for production upgrades, PostgreSQL migration SQL is the meaningful upgrade path; SQLite greenfield fallback artifacts are generated for tree completeness, not as a guaranteed runnable SQLite upgrade path
|
||||
|
||||
### `db_gen_orm_serdes_and_zod`
|
||||
|
||||
Purpose:
|
||||
|
||||
- aggregate generation target
|
||||
|
||||
Runs:
|
||||
|
||||
- `db_gen_ts`
|
||||
- `db_gen_zod`
|
||||
- `db_gen_cpp_headers`
|
||||
- `db_gen_odb_logic`
|
||||
- `db_gen_sql_ddl`
|
||||
|
||||
Output:
|
||||
|
||||
- no unique output of its own
|
||||
- produces the union of the five generation targets above
|
||||
|
||||
### `db_createfrom`
|
||||
|
||||
Purpose:
|
||||
|
||||
- recreates a live `dev` or `prod` database from pre-generated SQL DDL artifacts
|
||||
|
||||
Primary variables:
|
||||
|
||||
- `DB_TARGET`
|
||||
- `DB_CREATEFROM_SCHEMA_DIR`
|
||||
- one backend mapping for the selected target
|
||||
|
||||
Behavior:
|
||||
|
||||
- `DB_TARGET=proddev` is illegal and aborts
|
||||
- validates that `DB_CREATEFROM_SCHEMA_DIR` exists and has `openapi/openapi.yaml`
|
||||
- chooses backend by inspecting the selected target mapping
|
||||
- requires non-empty SQL files under:
|
||||
- `<schema>/generated-sql-ddl/sqlite/`, or
|
||||
- `<schema>/generated-sql-ddl/postgre/`
|
||||
- SQLite path:
|
||||
- deletes the current DB file if it exists
|
||||
- recreates parent directories
|
||||
- applies non-empty `.sql` files in sorted order using `sqlite3`
|
||||
- PostgreSQL path:
|
||||
- resets the `public` schema in the target DB
|
||||
- applies non-empty `.sql` files in sorted order using `psql`
|
||||
|
||||
Output:
|
||||
|
||||
- a recreated live database matching the chosen schema snapshot
|
||||
|
||||
### `db_migrate`
|
||||
|
||||
Purpose:
|
||||
|
||||
- applies a generated migration directory, plus optional backfill hooks, to the selected live DB target
|
||||
|
||||
Primary variables:
|
||||
|
||||
- `DB_TARGET`
|
||||
- `DB_MIGRATE_WITH`
|
||||
- `DB_MIGRATE_PRODDEV_USE_STALE`
|
||||
- one backend mapping for the selected target
|
||||
- optional clone hook variable for `proddev`
|
||||
|
||||
Behavior:
|
||||
|
||||
- validates that `<CPPBESSOT_WORKDIR>/migrations/<DB_MIGRATE_WITH>` exists
|
||||
- chooses backend by inspecting the selected target mapping
|
||||
- for `DB_TARGET=proddev`:
|
||||
- if `DB_MIGRATE_PRODDEV_USE_STALE=OFF`, runs the configured clone command first
|
||||
- if `DB_MIGRATE_PRODDEV_USE_STALE=ON`, requires that the stale proddev target already exists
|
||||
- hook order:
|
||||
1. `pre-structural-backfill.sh` if present
|
||||
2. non-empty structural SQL files for the selected backend if present
|
||||
3. `post-structural-backfill.sh` if present
|
||||
- hooks run with these environment variables:
|
||||
- `CPPBESSOT_DB_TARGET`
|
||||
- `CPPBESSOT_DB_BACKEND`
|
||||
- `CPPBESSOT_DB_MIGRATION_DIR`
|
||||
- `CPPBESSOT_DB_MIGRATE_WITH`
|
||||
- `CPPBESSOT_DB_SCHEMA_DIR_TO_GENERATE`
|
||||
- `CPPBESSOT_DB_CREATEFROM_SCHEMA_DIR`
|
||||
- `CPPBESSOT_DB_SQLITE_PATH`
|
||||
- `CPPBESSOT_DB_PGSQL_CONNSTR`
|
||||
|
||||
Output:
|
||||
|
||||
- a migrated live database for the selected target
|
||||
|
||||
## Generated Library Targets
|
||||
|
||||
### `cppbessot_add_generated_cpp_model_libraries()`
|
||||
|
||||
Registers:
|
||||
|
||||
- `cppBeSsotOpenAiModelGen`
|
||||
- alias `cppbessot::openai_model_gen`
|
||||
|
||||
Behavior:
|
||||
|
||||
- depends on `db_gen_cpp_headers`
|
||||
- building a consumer that links `cppbessot::openai_model_gen` forces model generation first
|
||||
|
||||
Consumes generated output from:
|
||||
|
||||
- `<schema>/generated-cpp-source/include/`
|
||||
- `<schema>/generated-cpp-source/src/`
|
||||
|
||||
### `cppbessot_add_generated_odb_libraries()`
|
||||
|
||||
Registers:
|
||||
|
||||
- `cppBeSsotOdbSqlite`
|
||||
- `cppBeSsotOdbPgSql`
|
||||
- aliases:
|
||||
- `cppbessot::odb_sqlite`
|
||||
- `cppbessot::odb_pgsql`
|
||||
|
||||
Behavior:
|
||||
|
||||
- depends on `db_gen_odb_logic`
|
||||
- also depends on `db_gen_sql_ddl` so ORM libs stay aligned with the same schema generation pass
|
||||
- building a consumer that links these libs forces ODB generation first
|
||||
|
||||
Consumes generated output from:
|
||||
|
||||
- `<schema>/generated-cpp-source/include/`
|
||||
- `<schema>/generated-odb-source/sqlite/`
|
||||
- `<schema>/generated-odb-source/postgre/`
|
||||
|
||||
### `cppbessot_add_generated_libraries()`
|
||||
|
||||
Registers all three generated libraries above.
|
||||
|
||||
This is the umbrella library-registration entry point used by `cppbessot_enable()`.
|
||||
|
||||
## Sample Workflows
|
||||
|
||||
### 1. Generate Everything For One Schema
|
||||
|
||||
```bash
|
||||
cmake -S . -B build \
|
||||
-DDB_SCHEMA_DIR_TO_GENERATE=v1.2
|
||||
|
||||
cmake --build build --target db_gen_orm_serdes_and_zod
|
||||
```
|
||||
|
||||
This produces:
|
||||
|
||||
- `generated-ts-types`
|
||||
- `generated-zod`
|
||||
- `generated-cpp-source`
|
||||
- `generated-odb-source`
|
||||
- `generated-sql-ddl`
|
||||
|
||||
### 2. Generate Migration Artifacts Between Two Schemas
|
||||
|
||||
```bash
|
||||
cmake -S . -B build \
|
||||
-DDB_SCHEMA_DIR_TO_GENERATE=v1.2 \
|
||||
-DDB_SCHEMA_DIR_MIGRATION_FROM=v1.1 \
|
||||
-DDB_SCHEMA_DIR_MIGRATION_TO=v1.2
|
||||
|
||||
cmake --build build --target db_gen_migrations
|
||||
```
|
||||
|
||||
This writes migration SQL under:
|
||||
|
||||
- `db/migrations/v1.1-v1.2/sqlite/`
|
||||
- `db/migrations/v1.1-v1.2/postgre/`
|
||||
|
||||
### 3. Link Only The Generated C++ Model Library
|
||||
|
||||
```cmake
|
||||
target_link_libraries(my_app PRIVATE cppbessot::openai_model_gen)
|
||||
```
|
||||
|
||||
That is valid if your application wants generated C++ models and JSON serdes but does not want ODB ORM libraries.
|
||||
|
||||
### 4. Link The Full Generated C++ Stack
|
||||
|
||||
```cmake
|
||||
target_link_libraries(my_app PRIVATE
|
||||
cppbessot::openai_model_gen
|
||||
cppbessot::odb_sqlite
|
||||
cppbessot::odb_pgsql
|
||||
)
|
||||
```
|
||||
|
||||
### 5. Create A Fresh SQLite `dev` Database From The Current Schema
|
||||
|
||||
```bash
|
||||
cmake -S . -B build \
|
||||
-DDB_SCHEMA_DIR_TO_GENERATE=v1.2 \
|
||||
-DDB_TARGET=dev \
|
||||
-DCPPBESSOT_DB_SQLITE_DEV_PATH=/tmp/myapp-dev.sqlite
|
||||
|
||||
cmake --build build --target db_gen_sql_ddl
|
||||
cmake --build build --target db_createfrom
|
||||
```
|
||||
|
||||
This recreates `/tmp/myapp-dev.sqlite` from:
|
||||
|
||||
- `db/v1.2/generated-sql-ddl/sqlite/`
|
||||
|
||||
### 6. Create A Fresh PostgreSQL `prod` Database Schema Snapshot
|
||||
|
||||
```bash
|
||||
cmake -S . -B build \
|
||||
-DDB_SCHEMA_DIR_TO_GENERATE=v1.2 \
|
||||
-DDB_TARGET=prod \
|
||||
-DCPPBESSOT_DB_PGSQL_PROD_CONNSTR="host=127.0.0.1 port=5432 dbname=myapp_prod user=postgres password=postgres"
|
||||
|
||||
cmake --build build --target db_gen_sql_ddl
|
||||
cmake --build build --target db_createfrom
|
||||
```
|
||||
|
||||
This resets the `public` schema in the target PostgreSQL DB, then reapplies the generated DDL.
|
||||
|
||||
### 7. Migrate A SQLite `dev` Database With Optional Backfills
|
||||
|
||||
```bash
|
||||
cmake -S . -B build \
|
||||
-DDB_SCHEMA_DIR_TO_GENERATE=v1.2 \
|
||||
-DDB_TARGET=dev \
|
||||
-DDB_MIGRATE_WITH=v1.1-v1.2 \
|
||||
-DCPPBESSOT_DB_SQLITE_DEV_PATH=/tmp/myapp-dev.sqlite
|
||||
|
||||
cmake --build build --target db_migrate
|
||||
```
|
||||
|
||||
If present, these run in order:
|
||||
|
||||
1. `db/migrations/v1.1-v1.2/pre-structural-backfill.sh`
|
||||
2. non-empty SQL files under `db/migrations/v1.1-v1.2/sqlite/`
|
||||
3. `db/migrations/v1.1-v1.2/post-structural-backfill.sh`
|
||||
|
||||
### 8. Migrate A `proddev` Clone From `prod`
|
||||
|
||||
SQLite example:
|
||||
|
||||
```bash
|
||||
cmake -S . -B build \
|
||||
-DDB_SCHEMA_DIR_TO_GENERATE=v1.2 \
|
||||
-DDB_TARGET=proddev \
|
||||
-DDB_MIGRATE_WITH=v1.1-v1.2 \
|
||||
-DCPPBESSOT_DB_SQLITE_PROD_PATH=/srv/myapp/prod.sqlite \
|
||||
-DCPPBESSOT_DB_SQLITE_PRODDEV_PATH=/srv/myapp/proddev.sqlite \
|
||||
-DCPPBESSOT_DB_SQLITE_CLONE_PROD_TO_PRODDEV_COMMAND='cp /srv/myapp/prod.sqlite /srv/myapp/proddev.sqlite'
|
||||
|
||||
cmake --build build --target db_migrate
|
||||
```
|
||||
|
||||
PostgreSQL example:
|
||||
|
||||
```bash
|
||||
cmake -S . -B build \
|
||||
-DDB_SCHEMA_DIR_TO_GENERATE=v1.2 \
|
||||
-DDB_TARGET=proddev \
|
||||
-DDB_MIGRATE_WITH=v1.1-v1.2 \
|
||||
-DCPPBESSOT_DB_PGSQL_PROD_CONNSTR="host=127.0.0.1 port=5432 dbname=myapp_prod user=postgres password=postgres" \
|
||||
-DCPPBESSOT_DB_PGSQL_PRODDEV_CONNSTR="host=127.0.0.1 port=5432 dbname=myapp_proddev user=postgres password=postgres" \
|
||||
-DCPPBESSOT_DB_PGSQL_CLONE_PROD_TO_PRODDEV_COMMAND='psql "host=127.0.0.1 port=5432 dbname=postgres user=postgres password=postgres" -v ON_ERROR_STOP=1 -c "DROP DATABASE IF EXISTS myapp_proddev;" -c "CREATE DATABASE myapp_proddev TEMPLATE myapp_prod;"'
|
||||
|
||||
cmake --build build --target db_migrate
|
||||
```
|
||||
|
||||
### 9. Reuse An Existing Stale `proddev`
|
||||
|
||||
```bash
|
||||
cmake -S . -B build \
|
||||
-DDB_SCHEMA_DIR_TO_GENERATE=v1.2 \
|
||||
-DDB_TARGET=proddev \
|
||||
-DDB_MIGRATE_WITH=v1.1-v1.2 \
|
||||
-DDB_MIGRATE_PRODDEV_USE_STALE=ON \
|
||||
-DCPPBESSOT_DB_SQLITE_PRODDEV_PATH=/srv/myapp/proddev.sqlite
|
||||
|
||||
cmake --build build --target db_migrate
|
||||
```
|
||||
|
||||
This skips the clone step and aborts if the stale target does not already exist.
|
||||
|
||||
## Testing
|
||||
|
||||
### Configure The Standalone Repo For Tests
|
||||
|
||||
```bash
|
||||
git -C cmake/cppbessot submodule update --init --recursive tests/googletest
|
||||
|
||||
cmake -S cmake/cppbessot -B build-cppbessot-tests \
|
||||
-DBUILD_TESTING=ON \
|
||||
-DDB_SCHEMA_DIR_TO_GENERATE=test-schema-v1.2
|
||||
```
|
||||
|
||||
### Run All Registered Tests
|
||||
|
||||
```bash
|
||||
ctest --test-dir build-cppbessot-tests --output-on-failure
|
||||
```
|
||||
|
||||
### ODB Runtime Tests
|
||||
|
||||
Provide DB target mappings:
|
||||
|
||||
```bash
|
||||
cmake -S cmake/cppbessot -B build-cppbessot-tests \
|
||||
-DBUILD_TESTING=ON \
|
||||
-DDB_SCHEMA_DIR_TO_GENERATE=test-schema-v1.2 \
|
||||
-DCPPBESSOT_DB_SQLITE_PRODDEV_PATH=/tmp/cppbessot-odb.sqlite \
|
||||
-DCPPBESSOT_DB_PGSQL_PRODDEV_CONNSTR="host=127.0.0.1 port=5432 dbname=cppbessot_odb_test user=postgres password=postgres"
|
||||
```
|
||||
|
||||
### Real PostgreSQL DB-Action Tests
|
||||
|
||||
These tests are only registered when all of the following are true:
|
||||
|
||||
- `BUILD_TESTING=ON`
|
||||
- `psql` is available
|
||||
- the required target connstr variables for the individual test are non-empty
|
||||
|
||||
Example:
|
||||
|
||||
```bash
|
||||
cmake -S cmake/cppbessot -B build-cppbessot-tests \
|
||||
-DBUILD_TESTING=ON \
|
||||
-DDB_SCHEMA_DIR_TO_GENERATE=test-schema-v1.2 \
|
||||
-DCPPBESSOT_DB_PGSQL_DEV_CONNSTR="host=127.0.0.1 port=5432 dbname=cppbessot_dev user=postgres password=postgres" \
|
||||
-DCPPBESSOT_DB_PGSQL_PROD_CONNSTR="host=127.0.0.1 port=5432 dbname=cppbessot_prod user=postgres password=postgres" \
|
||||
-DCPPBESSOT_DB_PGSQL_PRODDEV_CONNSTR="host=127.0.0.1 port=5432 dbname=cppbessot_proddev user=postgres password=postgres"
|
||||
|
||||
ctest --test-dir build-cppbessot-tests -R 'cppbessot_db_action_pgsql_.*_real' --output-on-failure
|
||||
```
|
||||
|
||||
## Notes
|
||||
|
||||
- Schema directory names must be basenames, not paths.
|
||||
- `DB_TARGET` must resolve to exactly one backend mapping.
|
||||
- `db_createfrom` and `db_migrate` operate on pre-generated SQL artifacts. If those artifacts are stale, regenerate them first.
|
||||
- The PostgreSQL live-action path resets the `public` schema. Use dedicated databases and be deliberate with `prod` mappings.
|
||||
|
||||
+133
-29
@@ -1,6 +1,5 @@
|
||||
include_guard(GLOBAL)
|
||||
|
||||
include(CMakeParseArguments)
|
||||
include("${CMAKE_CURRENT_LIST_DIR}/dbGenerationCommon.cmake")
|
||||
include("${CMAKE_CURRENT_LIST_DIR}/dbDependencyCheck.cmake")
|
||||
include("${CMAKE_CURRENT_LIST_DIR}/dbSchemaCheck.cmake")
|
||||
@@ -10,6 +9,10 @@ include("${CMAKE_CURRENT_LIST_DIR}/dbGenCpp.cmake")
|
||||
include("${CMAKE_CURRENT_LIST_DIR}/dbGenODB.cmake")
|
||||
include("${CMAKE_CURRENT_LIST_DIR}/dbGenSqlDDL.cmake")
|
||||
include("${CMAKE_CURRENT_LIST_DIR}/dbGenMigrations.cmake")
|
||||
include("${CMAKE_CURRENT_LIST_DIR}/dbActionCommon.cmake")
|
||||
include("${CMAKE_CURRENT_LIST_DIR}/dbActionCreateFrom.cmake")
|
||||
include("${CMAKE_CURRENT_LIST_DIR}/dbActionMigrate.cmake")
|
||||
include("${CMAKE_CURRENT_LIST_DIR}/dbRuntimeEnv.cmake")
|
||||
|
||||
if(NOT DEFINED CPPBESSOT_WORKDIR)
|
||||
set(CPPBESSOT_WORKDIR "db" CACHE STRING "CppBeSSOT schema root folder")
|
||||
@@ -29,10 +32,64 @@ if(NOT DEFINED DB_SCHEMA_DIR_MIGRATION_TO)
|
||||
"Optional target schema directory basename for migration generation")
|
||||
endif()
|
||||
|
||||
if(NOT DEFINED CPPBESSOT_GEN_MIGRATION_BACKENDS)
|
||||
if(DEFINED CPPBESSOT_GEN_MIGRATIONS_BACKENDS)
|
||||
set(CPPBESSOT_GEN_MIGRATION_BACKENDS "${CPPBESSOT_GEN_MIGRATIONS_BACKENDS}")
|
||||
else()
|
||||
set(CPPBESSOT_GEN_MIGRATION_BACKENDS "sqlite;pgsql")
|
||||
endif()
|
||||
endif()
|
||||
|
||||
set(CPPBESSOT_GEN_MIGRATION_BACKENDS "${CPPBESSOT_GEN_MIGRATION_BACKENDS}" CACHE STRING
|
||||
"ODB backends for db_gen_migrations; supported values: sqlite, pgsql")
|
||||
set(CPPBESSOT_GEN_MIGRATIONS_BACKENDS "${CPPBESSOT_GEN_MIGRATION_BACKENDS}" CACHE STRING
|
||||
"Compatibility alias for CPPBESSOT_GEN_MIGRATION_BACKENDS" FORCE)
|
||||
|
||||
if(NOT DEFINED DB_SCHEMA_CHANGES_ARE_ERROR)
|
||||
option(DB_SCHEMA_CHANGES_ARE_ERROR "Treat dirty schema changes as hard CMake error" OFF)
|
||||
endif()
|
||||
|
||||
if(NOT DEFINED DB_TARGET)
|
||||
set(DB_TARGET "dev" CACHE STRING "Live DB target to act on: prod, proddev, dev, or tests")
|
||||
endif()
|
||||
|
||||
if(NOT DEFINED DB_CREATEFROM_SCHEMA_DIR)
|
||||
set(DB_CREATEFROM_SCHEMA_DIR "${DB_SCHEMA_DIR_TO_GENERATE}" CACHE STRING
|
||||
"Schema directory basename under CPPBESSOT_WORKDIR to create a live DB from")
|
||||
endif()
|
||||
|
||||
if(NOT DEFINED DB_MIGRATE_WITH)
|
||||
set(DB_MIGRATE_WITH "" CACHE STRING
|
||||
"Migration directory basename under CPPBESSOT_WORKDIR/migrations to apply to the selected DB target")
|
||||
endif()
|
||||
|
||||
if(NOT DEFINED DB_MIGRATE_PRODDEV_USE_STALE)
|
||||
option(DB_MIGRATE_PRODDEV_USE_STALE "Reuse an existing stale proddev clone instead of recloning from prod" OFF)
|
||||
endif()
|
||||
|
||||
set(CPPBESSOT_DB_SQLITE_PROD_PATH "${CPPBESSOT_DB_SQLITE_PROD_PATH}" CACHE STRING
|
||||
"Parent-supplied SQLite DB path for DB_TARGET=prod")
|
||||
set(CPPBESSOT_DB_SQLITE_DEV_PATH "${CPPBESSOT_DB_SQLITE_DEV_PATH}" CACHE STRING
|
||||
"Parent-supplied SQLite DB path for DB_TARGET=dev")
|
||||
set(CPPBESSOT_DB_SQLITE_PRODDEV_PATH "${CPPBESSOT_DB_SQLITE_PRODDEV_PATH}" CACHE STRING
|
||||
"Parent-supplied SQLite DB path for DB_TARGET=proddev")
|
||||
set(CPPBESSOT_DB_SQLITE_TESTS_PATH "${CPPBESSOT_DB_SQLITE_TESTS_PATH}" CACHE STRING
|
||||
"Parent-supplied SQLite DB path for DB_TARGET=tests")
|
||||
set(CPPBESSOT_DB_PGSQL_PROD_CONNSTR "${CPPBESSOT_DB_PGSQL_PROD_CONNSTR}" CACHE STRING
|
||||
"Parent-supplied PostgreSQL connection string for DB_TARGET=prod")
|
||||
set(CPPBESSOT_DB_PGSQL_DEV_CONNSTR "${CPPBESSOT_DB_PGSQL_DEV_CONNSTR}" CACHE STRING
|
||||
"Parent-supplied PostgreSQL connection string for DB_TARGET=dev")
|
||||
set(CPPBESSOT_DB_PGSQL_PRODDEV_CONNSTR "${CPPBESSOT_DB_PGSQL_PRODDEV_CONNSTR}" CACHE STRING
|
||||
"Parent-supplied PostgreSQL connection string for DB_TARGET=proddev")
|
||||
set(CPPBESSOT_DB_PGSQL_TESTS_CONNSTR "${CPPBESSOT_DB_PGSQL_TESTS_CONNSTR}" CACHE STRING
|
||||
"Parent-supplied PostgreSQL connection string for DB_TARGET=tests")
|
||||
set(CPPBESSOT_DB_SQLITE_CLONE_PROD_TO_PRODDEV_COMMAND "${CPPBESSOT_DB_SQLITE_CLONE_PROD_TO_PRODDEV_COMMAND}" CACHE STRING
|
||||
"Parent-supplied command string that clones the prod SQLite DB into proddev")
|
||||
set(CPPBESSOT_DB_PGSQL_CLONE_PROD_TO_PRODDEV_COMMAND "${CPPBESSOT_DB_PGSQL_CLONE_PROD_TO_PRODDEV_COMMAND}" CACHE STRING
|
||||
"Parent-supplied command string that clones the prod PostgreSQL DB into proddev")
|
||||
|
||||
cppbessot_write_runtime_env_file("${CMAKE_BINARY_DIR}/cppbessot.env")
|
||||
|
||||
if(NOT DEFINED CPPBESSOT_AUTO_ENABLE)
|
||||
option(CPPBESSOT_AUTO_ENABLE "Auto-register CppBeSSOT targets when this file is included" ON)
|
||||
endif()
|
||||
@@ -48,6 +105,20 @@ function(_cppbessot_try_link_nlohmann target_name)
|
||||
endif()
|
||||
endfunction()
|
||||
|
||||
function(_cppbessot_assert_generation_targets_registered consumer_name)
|
||||
# Purpose: Ensure split public library registration functions are only used
|
||||
# after generation targets are registered (typically via cppbessot_enable()).
|
||||
# Inputs:
|
||||
# - consumer_name: Human-readable function name for diagnostics.
|
||||
# Outputs:
|
||||
# - No return value; raises FATAL_ERROR when prerequisites are missing.
|
||||
if(NOT TARGET db_gen_cpp_headers OR NOT TARGET db_gen_odb_logic OR NOT TARGET db_gen_sql_ddl)
|
||||
message(FATAL_ERROR
|
||||
"${consumer_name} requires generation targets to be registered first. "
|
||||
"Call cppbessot_enable() before invoking split generated-library registration functions.")
|
||||
endif()
|
||||
endfunction()
|
||||
|
||||
function(_cppbessot_add_generated_model_library cpp_include_dir expected_model_headers expected_model_sources)
|
||||
set_source_files_properties(${expected_model_headers} ${expected_model_sources}
|
||||
PROPERTIES GENERATED TRUE)
|
||||
@@ -111,42 +182,52 @@ function(_cppbessot_add_generated_pgsql_library
|
||||
add_library(cppbessot::odb_pgsql ALIAS cppBeSsotOdbPgSql)
|
||||
endfunction()
|
||||
|
||||
function(cppbessot_add_generated_libraries)
|
||||
# Purpose: Create consumable libraries from generated model and ODB sources.
|
||||
function(cppbessot_add_generated_cpp_model_libraries)
|
||||
# Purpose: Create consumable C++ model library from generated model sources.
|
||||
# Inputs:
|
||||
# - SCHEMA_DIR (optional named arg): Schema directory basename to consume.
|
||||
# - DB_SCHEMA_DIR_TO_GENERATE (fallback): Default schema directory basename.
|
||||
# - DB_SCHEMA_DIR_TO_GENERATE: Schema directory basename under CPPBESSOT_WORKDIR.
|
||||
# Outputs:
|
||||
# - Library targets (when sources exist):
|
||||
# - Library target:
|
||||
# - cppBeSsotOpenAiModelGen
|
||||
# - cppBeSsotOdbSqlite
|
||||
# - cppBeSsotOdbPgSql
|
||||
# - Alias targets:
|
||||
# - Alias target:
|
||||
# - cppbessot::openai_model_gen
|
||||
# - cppbessot::odb_sqlite
|
||||
# - cppbessot::odb_pgsql
|
||||
# - Emits warnings if expected source sets are missing.
|
||||
set(options)
|
||||
set(one_value_args SCHEMA_DIR)
|
||||
set(multi_value_args)
|
||||
cmake_parse_arguments(CPPB "${options}" "${one_value_args}" "${multi_value_args}" ${ARGN})
|
||||
|
||||
if(NOT CPPB_SCHEMA_DIR)
|
||||
set(CPPB_SCHEMA_DIR "${DB_SCHEMA_DIR_TO_GENERATE}")
|
||||
endif()
|
||||
|
||||
cppbessot_validate_schema_dir_name("${CPPB_SCHEMA_DIR}")
|
||||
cppbessot_get_schema_dir_path(_version_dir "${CPPB_SCHEMA_DIR}")
|
||||
|
||||
_cppbessot_assert_generation_targets_registered("cppbessot_add_generated_cpp_model_libraries")
|
||||
cppbessot_validate_schema_dir_name("${DB_SCHEMA_DIR_TO_GENERATE}")
|
||||
cppbessot_get_schema_dir_path(_version_dir "${DB_SCHEMA_DIR_TO_GENERATE}")
|
||||
set(_cpp_include_dir "${_version_dir}/generated-cpp-source/include")
|
||||
set(_model_leaf_include_dir "${_cpp_include_dir}/cppbessot/model")
|
||||
cppbessot_get_expected_cpp_model_outputs(_expected_model_headers _expected_model_sources "${CPPB_SCHEMA_DIR}")
|
||||
cppbessot_get_expected_odb_outputs(_expected_sqlite_odb_sources _expected_pgsql_odb_sources "${CPPB_SCHEMA_DIR}")
|
||||
cppbessot_get_expected_cpp_model_outputs(
|
||||
_expected_model_headers
|
||||
_expected_model_sources
|
||||
"${DB_SCHEMA_DIR_TO_GENERATE}")
|
||||
|
||||
_cppbessot_add_generated_model_library(
|
||||
"${_cpp_include_dir}"
|
||||
"${_expected_model_headers}"
|
||||
"${_expected_model_sources}")
|
||||
endfunction()
|
||||
|
||||
function(cppbessot_add_generated_odb_libraries)
|
||||
# Purpose: Create consumable ODB libraries from generated ODB sources.
|
||||
# Inputs:
|
||||
# - DB_SCHEMA_DIR_TO_GENERATE: Schema directory basename under CPPBESSOT_WORKDIR.
|
||||
# Outputs:
|
||||
# - Library targets:
|
||||
# - cppBeSsotOdbSqlite
|
||||
# - cppBeSsotOdbPgSql
|
||||
# - Alias targets:
|
||||
# - cppbessot::odb_sqlite
|
||||
# - cppbessot::odb_pgsql
|
||||
_cppbessot_assert_generation_targets_registered("cppbessot_add_generated_odb_libraries")
|
||||
cppbessot_validate_schema_dir_name("${DB_SCHEMA_DIR_TO_GENERATE}")
|
||||
cppbessot_get_schema_dir_path(_version_dir "${DB_SCHEMA_DIR_TO_GENERATE}")
|
||||
|
||||
set(_cpp_include_dir "${_version_dir}/generated-cpp-source/include")
|
||||
set(_model_leaf_include_dir "${_cpp_include_dir}/cppbessot/model")
|
||||
cppbessot_get_expected_odb_outputs(
|
||||
_expected_sqlite_odb_sources
|
||||
_expected_pgsql_odb_sources
|
||||
"${DB_SCHEMA_DIR_TO_GENERATE}")
|
||||
|
||||
_cppbessot_add_generated_sqlite_library(
|
||||
"${_cpp_include_dir}"
|
||||
"${_model_leaf_include_dir}"
|
||||
@@ -159,6 +240,23 @@ function(cppbessot_add_generated_libraries)
|
||||
"${_expected_pgsql_odb_sources}")
|
||||
endfunction()
|
||||
|
||||
function(cppbessot_add_generated_libraries)
|
||||
# Purpose: Create consumable libraries from generated model and ODB sources.
|
||||
# Inputs:
|
||||
# - DB_SCHEMA_DIR_TO_GENERATE: Schema directory basename under CPPBESSOT_WORKDIR.
|
||||
# Outputs:
|
||||
# - Library targets (when sources exist):
|
||||
# - cppBeSsotOpenAiModelGen
|
||||
# - cppBeSsotOdbSqlite
|
||||
# - cppBeSsotOdbPgSql
|
||||
# - Alias targets:
|
||||
# - cppbessot::openai_model_gen
|
||||
# - cppbessot::odb_sqlite
|
||||
# - cppbessot::odb_pgsql
|
||||
cppbessot_add_generated_cpp_model_libraries()
|
||||
cppbessot_add_generated_odb_libraries()
|
||||
endfunction()
|
||||
|
||||
function(cppbessot_enable)
|
||||
# Purpose: Entry-point orchestration for dependency checks, custom generation
|
||||
# targets, aggregate targets, and generated library registration.
|
||||
@@ -167,6 +265,7 @@ function(cppbessot_enable)
|
||||
# - DB_SCHEMA_DIR_TO_GENERATE
|
||||
# - DB_SCHEMA_DIR_MIGRATION_FROM
|
||||
# - DB_SCHEMA_DIR_MIGRATION_TO
|
||||
# - CPPBESSOT_GEN_MIGRATION_BACKENDS
|
||||
# - DB_SCHEMA_CHANGES_ARE_ERROR (optional behavior control)
|
||||
# Outputs:
|
||||
# - Custom targets:
|
||||
@@ -178,11 +277,14 @@ function(cppbessot_enable)
|
||||
# - db_gen_sql_ddl
|
||||
# - db_gen_migrations
|
||||
# - db_gen_orm_serdes_and_zod
|
||||
# - db_createfrom
|
||||
# - db_migrate
|
||||
# - Generated library targets for selected schema version.
|
||||
cppbessot_initialize_paths()
|
||||
|
||||
cppbessot_validate_schema_dir_name("${DB_SCHEMA_DIR_TO_GENERATE}")
|
||||
cppbessot_assert_schema_dir_exists("${DB_SCHEMA_DIR_TO_GENERATE}")
|
||||
cppbessot_assert_openapi_exists("${DB_SCHEMA_DIR_TO_GENERATE}")
|
||||
|
||||
cppbessot_check_dependencies()
|
||||
|
||||
@@ -206,7 +308,7 @@ function(cppbessot_enable)
|
||||
COMMAND "${CMAKE_COMMAND}" -E echo
|
||||
"Set DB_SCHEMA_DIR_MIGRATION_FROM and DB_SCHEMA_DIR_MIGRATION_TO to enable migration generation."
|
||||
COMMAND "${CMAKE_COMMAND}" -E false
|
||||
VERBATIM
|
||||
VERBATIM
|
||||
)
|
||||
set_target_properties(db_gen_migrations PROPERTIES EXCLUDE_FROM_ALL TRUE)
|
||||
endif()
|
||||
@@ -220,7 +322,9 @@ function(cppbessot_enable)
|
||||
db_gen_sql_ddl)
|
||||
set_target_properties(db_gen_orm_serdes_and_zod PROPERTIES EXCLUDE_FROM_ALL TRUE)
|
||||
|
||||
cppbessot_add_generated_libraries(SCHEMA_DIR "${DB_SCHEMA_DIR_TO_GENERATE}")
|
||||
cppbessot_add_db_createfrom_target()
|
||||
cppbessot_add_db_migrate_target()
|
||||
cppbessot_add_generated_libraries()
|
||||
endfunction()
|
||||
|
||||
if(CPPBESSOT_AUTO_ENABLE)
|
||||
|
||||
@@ -0,0 +1,55 @@
|
||||
include_guard(GLOBAL)
|
||||
|
||||
include("${CMAKE_CURRENT_LIST_DIR}/dbGenerationCommon.cmake")
|
||||
include("${CMAKE_CURRENT_LIST_DIR}/dbActionShared.cmake")
|
||||
|
||||
function(cppbessot_validate_db_target db_target)
|
||||
_cppbessot_db_action_validate_db_target_impl("${db_target}")
|
||||
endfunction()
|
||||
|
||||
function(cppbessot_validate_migration_dir_name migration_dir)
|
||||
_cppbessot_db_action_validate_basename(
|
||||
"${migration_dir}"
|
||||
"Migration directory name"
|
||||
"CPPBESSOT_WORKDIR/migrations")
|
||||
endfunction()
|
||||
|
||||
function(cppbessot_get_migration_dir_path out_var migration_dir)
|
||||
cppbessot_validate_migration_dir_name("${migration_dir}")
|
||||
cppbessot_abs_path(_workdir "${CPPBESSOT_WORKDIR}")
|
||||
set(${out_var} "${_workdir}/migrations/${migration_dir}" PARENT_SCOPE)
|
||||
endfunction()
|
||||
|
||||
function(cppbessot_assert_migration_dir_exists migration_dir)
|
||||
cppbessot_get_migration_dir_path(_migration_dir_path "${migration_dir}")
|
||||
if(NOT IS_DIRECTORY "${_migration_dir_path}")
|
||||
message(FATAL_ERROR "Migration directory does not exist: ${_migration_dir_path}")
|
||||
endif()
|
||||
endfunction()
|
||||
|
||||
function(_cppbessot_db_action_common_cache_args out_var)
|
||||
set(_args
|
||||
"-DCPPBESSOT_PROJECT_SOURCE_DIR=${PROJECT_SOURCE_DIR}"
|
||||
"-DCPPBESSOT_WORKDIR=${CPPBESSOT_WORKDIR}"
|
||||
"-DDB_SCHEMA_DIR_TO_GENERATE=${DB_SCHEMA_DIR_TO_GENERATE}"
|
||||
"-DDB_CREATEFROM_SCHEMA_DIR=${DB_CREATEFROM_SCHEMA_DIR}"
|
||||
"-DDB_TARGET=${DB_TARGET}"
|
||||
"-DCPPBESSOT_DB_SQLITE_PROD_PATH=${CPPBESSOT_DB_SQLITE_PROD_PATH}"
|
||||
"-DCPPBESSOT_DB_SQLITE_DEV_PATH=${CPPBESSOT_DB_SQLITE_DEV_PATH}"
|
||||
"-DCPPBESSOT_DB_SQLITE_PRODDEV_PATH=${CPPBESSOT_DB_SQLITE_PRODDEV_PATH}"
|
||||
"-DCPPBESSOT_DB_SQLITE_TESTS_PATH=${CPPBESSOT_DB_SQLITE_TESTS_PATH}"
|
||||
"-DCPPBESSOT_DB_PGSQL_PROD_CONNSTR=${CPPBESSOT_DB_PGSQL_PROD_CONNSTR}"
|
||||
"-DCPPBESSOT_DB_PGSQL_DEV_CONNSTR=${CPPBESSOT_DB_PGSQL_DEV_CONNSTR}"
|
||||
"-DCPPBESSOT_DB_PGSQL_PRODDEV_CONNSTR=${CPPBESSOT_DB_PGSQL_PRODDEV_CONNSTR}"
|
||||
"-DCPPBESSOT_DB_PGSQL_TESTS_CONNSTR=${CPPBESSOT_DB_PGSQL_TESTS_CONNSTR}")
|
||||
set(${out_var} "${_args}" PARENT_SCOPE)
|
||||
endfunction()
|
||||
|
||||
function(_cppbessot_add_db_action_target target_name script_path comment_text)
|
||||
add_custom_target(${target_name}
|
||||
COMMAND "${CMAKE_COMMAND}" ${ARGN} -P "${script_path}"
|
||||
COMMENT "${comment_text}"
|
||||
VERBATIM
|
||||
)
|
||||
set_target_properties(${target_name} PROPERTIES EXCLUDE_FROM_ALL TRUE)
|
||||
endfunction()
|
||||
@@ -0,0 +1,13 @@
|
||||
include_guard(GLOBAL)
|
||||
|
||||
include("${CMAKE_CURRENT_LIST_DIR}/dbActionCommon.cmake")
|
||||
set(_CPPBESSOT_DB_ACTION_CREATEFROM_DIR "${CMAKE_CURRENT_LIST_DIR}")
|
||||
|
||||
function(cppbessot_add_db_createfrom_target)
|
||||
_cppbessot_db_action_common_cache_args(_common_args)
|
||||
_cppbessot_add_db_action_target(
|
||||
db_createfrom
|
||||
"${_CPPBESSOT_DB_ACTION_CREATEFROM_DIR}/scripts/run_db_createfrom.cmake"
|
||||
"Creating live DB target `${DB_TARGET}` from schema `${DB_CREATEFROM_SCHEMA_DIR}`"
|
||||
${_common_args})
|
||||
endfunction()
|
||||
@@ -0,0 +1,17 @@
|
||||
include_guard(GLOBAL)
|
||||
|
||||
include("${CMAKE_CURRENT_LIST_DIR}/dbActionCommon.cmake")
|
||||
set(_CPPBESSOT_DB_ACTION_MIGRATE_DIR "${CMAKE_CURRENT_LIST_DIR}")
|
||||
|
||||
function(cppbessot_add_db_migrate_target)
|
||||
_cppbessot_db_action_common_cache_args(_common_args)
|
||||
_cppbessot_add_db_action_target(
|
||||
db_migrate
|
||||
"${_CPPBESSOT_DB_ACTION_MIGRATE_DIR}/scripts/run_db_migrate.cmake"
|
||||
"Migrating live DB target `${DB_TARGET}` using migration `${DB_MIGRATE_WITH}`"
|
||||
${_common_args}
|
||||
"-DDB_MIGRATE_WITH=${DB_MIGRATE_WITH}"
|
||||
"-DDB_MIGRATE_PRODDEV_USE_STALE=${DB_MIGRATE_PRODDEV_USE_STALE}"
|
||||
"-DCPPBESSOT_DB_SQLITE_CLONE_PROD_TO_PRODDEV_COMMAND=${CPPBESSOT_DB_SQLITE_CLONE_PROD_TO_PRODDEV_COMMAND}"
|
||||
"-DCPPBESSOT_DB_PGSQL_CLONE_PROD_TO_PRODDEV_COMMAND=${CPPBESSOT_DB_PGSQL_CLONE_PROD_TO_PRODDEV_COMMAND}")
|
||||
endfunction()
|
||||
@@ -0,0 +1,21 @@
|
||||
include_guard(GLOBAL)
|
||||
|
||||
function(_cppbessot_db_action_validate_basename value kind relative_root)
|
||||
if("${value}" STREQUAL "")
|
||||
message(FATAL_ERROR "${kind} must not be empty.")
|
||||
endif()
|
||||
|
||||
if("${value}" MATCHES "[/\\\\]")
|
||||
message(FATAL_ERROR
|
||||
"${kind} `${value}` must be a basename under ${relative_root}, not a path.")
|
||||
endif()
|
||||
endfunction()
|
||||
|
||||
function(_cppbessot_db_action_validate_db_target_impl db_target)
|
||||
if(NOT "${db_target}" STREQUAL "prod"
|
||||
AND NOT "${db_target}" STREQUAL "proddev"
|
||||
AND NOT "${db_target}" STREQUAL "dev"
|
||||
AND NOT "${db_target}" STREQUAL "tests")
|
||||
message(FATAL_ERROR "DB_TARGET must be one of: prod, proddev, dev, tests.")
|
||||
endif()
|
||||
endfunction()
|
||||
@@ -4,6 +4,23 @@ include("${CMAKE_CURRENT_LIST_DIR}/dbGenerationCommon.cmake")
|
||||
include(CheckIncludeFileCXX)
|
||||
include(CMakePushCheckState)
|
||||
|
||||
function(_cppbessot_publish_dependency_outputs)
|
||||
set(CPPBESSOT_ODB_EXECUTABLE "${CPPBESSOT_ODB_EXECUTABLE}" PARENT_SCOPE)
|
||||
set(CPPBESSOT_NPX_EXECUTABLE "${CPPBESSOT_NPX_EXECUTABLE}" PARENT_SCOPE)
|
||||
set(CPPBESSOT_NPM_EXECUTABLE "${CPPBESSOT_NPM_EXECUTABLE}" PARENT_SCOPE)
|
||||
set(CPPBESSOT_JAVA_EXECUTABLE "${CPPBESSOT_JAVA_EXECUTABLE}" PARENT_SCOPE)
|
||||
set(CPPBESSOT_GIT_EXECUTABLE "${CPPBESSOT_GIT_EXECUTABLE}" PARENT_SCOPE)
|
||||
set(CPPBESSOT_SQLITE3_EXECUTABLE "${CPPBESSOT_SQLITE3_EXECUTABLE}" PARENT_SCOPE)
|
||||
set(CPPBESSOT_ODB_RUNTIME_LIB "${CPPBESSOT_ODB_RUNTIME_LIB}" PARENT_SCOPE)
|
||||
set(CPPBESSOT_ODB_SQLITE_RUNTIME_LIB "${CPPBESSOT_ODB_SQLITE_RUNTIME_LIB}" PARENT_SCOPE)
|
||||
set(CPPBESSOT_ODB_PGSQL_RUNTIME_LIB "${CPPBESSOT_ODB_PGSQL_RUNTIME_LIB}" PARENT_SCOPE)
|
||||
set(CPPBESSOT_SQLITE_INCLUDE_DIR "${CPPBESSOT_SQLITE_INCLUDE_DIR}" PARENT_SCOPE)
|
||||
set(CPPBESSOT_PGSQL_INCLUDE_DIR "${CPPBESSOT_PGSQL_INCLUDE_DIR}" PARENT_SCOPE)
|
||||
set(CPPBESSOT_SQLITE_CLIENT_LIB "${CPPBESSOT_SQLITE_CLIENT_LIB}" PARENT_SCOPE)
|
||||
set(CPPBESSOT_PGSQL_CLIENT_LIB "${CPPBESSOT_PGSQL_CLIENT_LIB}" PARENT_SCOPE)
|
||||
set(CPPBESSOT_OPENAPI_ZOD_AVAILABLE TRUE PARENT_SCOPE)
|
||||
endfunction()
|
||||
|
||||
function(_cppbessot_require_program var_name program_name hint)
|
||||
# Purpose: Locate an executable and fail with a clear install hint if missing.
|
||||
# Inputs:
|
||||
@@ -20,6 +37,17 @@ function(_cppbessot_require_program var_name program_name hint)
|
||||
endif()
|
||||
endfunction()
|
||||
|
||||
function(_cppbessot_any_nonempty_var out_var)
|
||||
foreach(_var_name IN LISTS ARGN)
|
||||
if(DEFINED ${_var_name} AND NOT "${${_var_name}}" STREQUAL "")
|
||||
set(${out_var} TRUE PARENT_SCOPE)
|
||||
return()
|
||||
endif()
|
||||
endforeach()
|
||||
|
||||
set(${out_var} FALSE PARENT_SCOPE)
|
||||
endfunction()
|
||||
|
||||
function(_cppbessot_require_npm_package npm_executable package_name)
|
||||
# Purpose: Ensure an npm package exists either locally in PROJECT_SOURCE_DIR
|
||||
# or globally in the active npm installation.
|
||||
@@ -28,9 +56,10 @@ function(_cppbessot_require_npm_package npm_executable package_name)
|
||||
# - package_name: Package name to validate.
|
||||
# Outputs:
|
||||
# - No return value; raises FATAL_ERROR when package is not installed.
|
||||
cppbessot_get_module_root(_module_root)
|
||||
execute_process(
|
||||
COMMAND "${npm_executable}" list --depth=0 "${package_name}"
|
||||
WORKING_DIRECTORY "${PROJECT_SOURCE_DIR}"
|
||||
WORKING_DIRECTORY "${_module_root}"
|
||||
RESULT_VARIABLE _local_result
|
||||
OUTPUT_QUIET
|
||||
ERROR_QUIET
|
||||
@@ -62,8 +91,10 @@ function(_cppbessot_require_npx_package_executable npx_executable package_execut
|
||||
# - package_executable: Executable name exposed by a package.
|
||||
# Outputs:
|
||||
# - No return value; raises FATAL_ERROR if execution fails.
|
||||
cppbessot_get_module_root(_module_root)
|
||||
execute_process(
|
||||
COMMAND "${npx_executable}" --no-install "${package_executable}" --help
|
||||
WORKING_DIRECTORY "${_module_root}"
|
||||
RESULT_VARIABLE _help_result
|
||||
OUTPUT_QUIET
|
||||
ERROR_VARIABLE _help_stderr
|
||||
@@ -76,6 +107,7 @@ function(_cppbessot_require_npx_package_executable npx_executable package_execut
|
||||
# Some CLIs return non-zero for --help; verify with version as fallback.
|
||||
execute_process(
|
||||
COMMAND "${npx_executable}" --no-install "${package_executable}" version
|
||||
WORKING_DIRECTORY "${_module_root}"
|
||||
RESULT_VARIABLE _version_result
|
||||
OUTPUT_QUIET
|
||||
ERROR_VARIABLE _version_stderr
|
||||
@@ -101,6 +133,12 @@ function(cppbessot_check_dependencies)
|
||||
# - CPPBESSOT_GIT_EXECUTABLE (PARENT_SCOPE)
|
||||
# - CPPBESSOT_OPENAPI_ZOD_AVAILABLE (PARENT_SCOPE)
|
||||
# - No return value; raises FATAL_ERROR on missing dependencies.
|
||||
get_property(_cppbessot_dependencies_checked GLOBAL PROPERTY CPPBESSOT_DEPENDENCIES_CHECKED)
|
||||
if(_cppbessot_dependencies_checked)
|
||||
_cppbessot_publish_dependency_outputs()
|
||||
return()
|
||||
endif()
|
||||
|
||||
_cppbessot_require_program(CPPBESSOT_ODB_EXECUTABLE odb
|
||||
"Install ODB compiler and ensure `odb` is in PATH.")
|
||||
_cppbessot_require_program(CPPBESSOT_NPX_EXECUTABLE npx
|
||||
@@ -111,6 +149,18 @@ function(cppbessot_check_dependencies)
|
||||
"Install a Java runtime (OpenAPI generator uses Java).")
|
||||
_cppbessot_require_program(CPPBESSOT_GIT_EXECUTABLE git
|
||||
"Install Git and ensure it is available in PATH.")
|
||||
_cppbessot_require_program(CPPBESSOT_SQLITE3_EXECUTABLE sqlite3
|
||||
"Install the SQLite CLI so live SQLite DB actions can apply SQL files.")
|
||||
_cppbessot_any_nonempty_var(
|
||||
_cppbessot_require_psql
|
||||
CPPBESSOT_DB_PGSQL_PROD_CONNSTR
|
||||
CPPBESSOT_DB_PGSQL_DEV_CONNSTR
|
||||
CPPBESSOT_DB_PGSQL_PRODDEV_CONNSTR
|
||||
CPPBESSOT_DB_PGSQL_TESTS_CONNSTR)
|
||||
if(_cppbessot_require_psql)
|
||||
_cppbessot_require_program(_cppbessot_psql_executable psql
|
||||
"Install the PostgreSQL client CLI so live PostgreSQL DB actions can run.")
|
||||
endif()
|
||||
|
||||
_cppbessot_require_npm_package("${CPPBESSOT_NPM_EXECUTABLE}" "@openapitools/openapi-generator-cli")
|
||||
_cppbessot_require_npx_package_executable("${CPPBESSOT_NPX_EXECUTABLE}" "@openapitools/openapi-generator-cli")
|
||||
@@ -187,17 +237,6 @@ function(cppbessot_check_dependencies)
|
||||
"PostgreSQL client library was not found. On Ubuntu/Debian install package `libpq-dev`.")
|
||||
endif()
|
||||
|
||||
set(CPPBESSOT_ODB_EXECUTABLE "${CPPBESSOT_ODB_EXECUTABLE}" PARENT_SCOPE)
|
||||
set(CPPBESSOT_NPX_EXECUTABLE "${CPPBESSOT_NPX_EXECUTABLE}" PARENT_SCOPE)
|
||||
set(CPPBESSOT_NPM_EXECUTABLE "${CPPBESSOT_NPM_EXECUTABLE}" PARENT_SCOPE)
|
||||
set(CPPBESSOT_JAVA_EXECUTABLE "${CPPBESSOT_JAVA_EXECUTABLE}" PARENT_SCOPE)
|
||||
set(CPPBESSOT_GIT_EXECUTABLE "${CPPBESSOT_GIT_EXECUTABLE}" PARENT_SCOPE)
|
||||
set(CPPBESSOT_ODB_RUNTIME_LIB "${CPPBESSOT_ODB_RUNTIME_LIB}" PARENT_SCOPE)
|
||||
set(CPPBESSOT_ODB_SQLITE_RUNTIME_LIB "${CPPBESSOT_ODB_SQLITE_RUNTIME_LIB}" PARENT_SCOPE)
|
||||
set(CPPBESSOT_ODB_PGSQL_RUNTIME_LIB "${CPPBESSOT_ODB_PGSQL_RUNTIME_LIB}" PARENT_SCOPE)
|
||||
set(CPPBESSOT_SQLITE_INCLUDE_DIR "${CPPBESSOT_SQLITE_INCLUDE_DIR}" PARENT_SCOPE)
|
||||
set(CPPBESSOT_PGSQL_INCLUDE_DIR "${CPPBESSOT_PGSQL_INCLUDE_DIR}" PARENT_SCOPE)
|
||||
set(CPPBESSOT_SQLITE_CLIENT_LIB "${CPPBESSOT_SQLITE_CLIENT_LIB}" PARENT_SCOPE)
|
||||
set(CPPBESSOT_PGSQL_CLIENT_LIB "${CPPBESSOT_PGSQL_CLIENT_LIB}" PARENT_SCOPE)
|
||||
set(CPPBESSOT_OPENAPI_ZOD_AVAILABLE TRUE PARENT_SCOPE)
|
||||
set_property(GLOBAL PROPERTY CPPBESSOT_DEPENDENCIES_CHECKED TRUE)
|
||||
_cppbessot_publish_dependency_outputs()
|
||||
endfunction()
|
||||
|
||||
+11
-7
@@ -1,7 +1,6 @@
|
||||
include_guard(GLOBAL)
|
||||
|
||||
include("${CMAKE_CURRENT_LIST_DIR}/dbGenerationCommon.cmake")
|
||||
set(_CPPBESSOT_DB_GEN_CPP_DIR "${CMAKE_CURRENT_LIST_DIR}")
|
||||
|
||||
function(cppbessot_add_db_gen_cpp_target schema_dir)
|
||||
# Purpose: Register C++ model generation target using checked-in templates.
|
||||
@@ -13,19 +12,20 @@ function(cppbessot_add_db_gen_cpp_target schema_dir)
|
||||
# - Files under `<schema_dir>/generated-cpp-source`.
|
||||
cppbessot_validate_schema_dir_name("${schema_dir}")
|
||||
cppbessot_get_schema_dir_path(_version_dir "${schema_dir}")
|
||||
if(DEFINED CPPBESSOT_MODULE_ROOT AND NOT "${CPPBESSOT_MODULE_ROOT}" STREQUAL "")
|
||||
set(_module_root "${CPPBESSOT_MODULE_ROOT}")
|
||||
else()
|
||||
get_filename_component(_module_root "${_CPPBESSOT_DB_GEN_CPP_DIR}/.." ABSOLUTE)
|
||||
endif()
|
||||
cppbessot_get_module_root(_module_root)
|
||||
|
||||
set(_openapi_file "${_version_dir}/openapi/openapi.yaml")
|
||||
set(_template_dir "${_module_root}/openapi/templates/cpp-odb-json")
|
||||
set(_template_config "${_template_dir}/config.yaml")
|
||||
set(_output_dir "${_version_dir}/generated-cpp-source")
|
||||
file(GLOB_RECURSE _template_inputs CONFIGURE_DEPENDS "${_template_dir}/*")
|
||||
cppbessot_get_expected_cpp_model_outputs(
|
||||
_expected_model_headers
|
||||
_expected_model_sources
|
||||
"${schema_dir}")
|
||||
|
||||
add_custom_target(db_gen_cpp_headers
|
||||
add_custom_command(
|
||||
OUTPUT ${_expected_model_headers} ${_expected_model_sources}
|
||||
COMMAND ${CMAKE_COMMAND} -E make_directory "${_output_dir}"
|
||||
COMMAND "${CPPBESSOT_NPX_EXECUTABLE}" @openapitools/openapi-generator-cli generate
|
||||
-i "${_openapi_file}"
|
||||
@@ -35,9 +35,13 @@ function(cppbessot_add_db_gen_cpp_target schema_dir)
|
||||
-o "${_output_dir}"
|
||||
--global-property models
|
||||
DEPENDS "${_openapi_file}" ${_template_inputs}
|
||||
WORKING_DIRECTORY "${_module_root}"
|
||||
COMMENT "Generating C++ model headers/sources for ${schema_dir}"
|
||||
VERBATIM
|
||||
)
|
||||
|
||||
add_custom_target(db_gen_cpp_headers
|
||||
DEPENDS ${_expected_model_headers} ${_expected_model_sources})
|
||||
|
||||
set_target_properties(db_gen_cpp_headers PROPERTIES EXCLUDE_FROM_ALL TRUE)
|
||||
endfunction()
|
||||
|
||||
@@ -9,6 +9,7 @@ function(cppbessot_add_db_gen_migrations_target from_schema_dir to_schema_dir)
|
||||
# - from_schema_dir: Source schema directory basename.
|
||||
# - to_schema_dir: Target schema directory basename.
|
||||
# - CPPBESSOT_ODB_EXECUTABLE: Path to `odb` compiler.
|
||||
# - CPPBESSOT_GEN_MIGRATION_BACKENDS: List of ODB backends to generate.
|
||||
# Outputs:
|
||||
# - CMake target: `db_gen_migrations` (EXCLUDE_FROM_ALL).
|
||||
# - Files under `migrations/<from>-<to>/{sqlite,postgre}`.
|
||||
@@ -26,10 +27,11 @@ function(cppbessot_add_db_gen_migrations_target from_schema_dir to_schema_dir)
|
||||
|
||||
add_custom_target(db_gen_migrations
|
||||
COMMAND "${CMAKE_COMMAND}"
|
||||
-DCPPBESSOT_ODB_EXECUTABLE=${CPPBESSOT_ODB_EXECUTABLE}
|
||||
-DCPPBESSOT_FROM_VERSION_DIR=${_from_dir}
|
||||
-DCPPBESSOT_TO_VERSION_DIR=${_to_dir}
|
||||
-DCPPBESSOT_MIGRATION_DIR=${_migration_dir}
|
||||
"-DCPPBESSOT_ODB_EXECUTABLE=${CPPBESSOT_ODB_EXECUTABLE}"
|
||||
"-DCPPBESSOT_FROM_VERSION_DIR=${_from_dir}"
|
||||
"-DCPPBESSOT_TO_VERSION_DIR=${_to_dir}"
|
||||
"-DCPPBESSOT_MIGRATION_DIR=${_migration_dir}"
|
||||
"-DCPPBESSOT_GEN_MIGRATION_BACKENDS=${CPPBESSOT_GEN_MIGRATION_BACKENDS}"
|
||||
-P "${_CPPBESSOT_DB_GEN_MIGRATIONS_DIR}/scripts/run_odb_migrations.cmake"
|
||||
COMMENT "Generating DB migrations: ${from_schema_dir} -> ${to_schema_dir}"
|
||||
VERBATIM
|
||||
|
||||
+12
-2
@@ -13,16 +13,26 @@ function(cppbessot_add_db_gen_odb_target schema_dir)
|
||||
# - Files under `<schema_dir>/generated-odb-source/{sqlite,postgre}`.
|
||||
cppbessot_validate_schema_dir_name("${schema_dir}")
|
||||
cppbessot_get_schema_dir_path(_version_dir "${schema_dir}")
|
||||
cppbessot_get_expected_odb_generation_artifacts(
|
||||
_expected_odb_model_headers
|
||||
_expected_odb_sources
|
||||
"${schema_dir}")
|
||||
_cppbessot_get_openapi_file_path(_openapi_file "${schema_dir}")
|
||||
|
||||
add_custom_target(db_gen_odb_logic
|
||||
add_custom_command(
|
||||
OUTPUT ${_expected_odb_sources}
|
||||
COMMAND "${CMAKE_COMMAND}"
|
||||
-DCPPBESSOT_ODB_EXECUTABLE=${CPPBESSOT_ODB_EXECUTABLE}
|
||||
-DCPPBESSOT_VERSION_DIR=${_version_dir}
|
||||
-P "${_CPPBESSOT_DB_GEN_ODB_DIR}/scripts/run_odb_logic.cmake"
|
||||
DEPENDS db_gen_cpp_headers
|
||||
DEPENDS "${_openapi_file}" ${_expected_odb_model_headers}
|
||||
COMMENT "Generating ODB ORM sources for ${schema_dir} (sqlite + postgre)"
|
||||
VERBATIM
|
||||
)
|
||||
|
||||
add_custom_target(db_gen_odb_logic
|
||||
DEPENDS ${_expected_odb_sources})
|
||||
add_dependencies(db_gen_odb_logic db_gen_cpp_headers)
|
||||
|
||||
set_target_properties(db_gen_odb_logic PROPERTIES EXCLUDE_FROM_ALL TRUE)
|
||||
endfunction()
|
||||
|
||||
+16
-2
@@ -13,16 +13,30 @@ function(cppbessot_add_db_gen_sql_ddl_target schema_dir)
|
||||
# - Files under `<schema_dir>/generated-sql-ddl/{sqlite,postgre}`.
|
||||
cppbessot_validate_schema_dir_name("${schema_dir}")
|
||||
cppbessot_get_schema_dir_path(_version_dir "${schema_dir}")
|
||||
cppbessot_get_expected_odb_generation_artifacts(
|
||||
_expected_model_headers
|
||||
_expected_odb_sources
|
||||
"${schema_dir}")
|
||||
|
||||
add_custom_target(db_gen_sql_ddl
|
||||
set(_stamp_dir "${CMAKE_CURRENT_BINARY_DIR}/cppbessot/${schema_dir}")
|
||||
set(_stamp_file "${_stamp_dir}/db_gen_sql_ddl.stamp")
|
||||
|
||||
add_custom_command(
|
||||
OUTPUT "${_stamp_file}"
|
||||
COMMAND "${CMAKE_COMMAND}" -E make_directory "${_stamp_dir}"
|
||||
COMMAND "${CMAKE_COMMAND}"
|
||||
-DCPPBESSOT_ODB_EXECUTABLE=${CPPBESSOT_ODB_EXECUTABLE}
|
||||
-DCPPBESSOT_VERSION_DIR=${_version_dir}
|
||||
-P "${_CPPBESSOT_DB_GEN_SQL_DDL_DIR}/scripts/run_odb_sql_ddl.cmake"
|
||||
DEPENDS db_gen_cpp_headers
|
||||
COMMAND "${CMAKE_COMMAND}" -E touch "${_stamp_file}"
|
||||
DEPENDS db_gen_cpp_headers ${_expected_model_headers}
|
||||
COMMENT "Generating SQL DDL snapshots for ${schema_dir} (sqlite + postgre)"
|
||||
VERBATIM
|
||||
)
|
||||
|
||||
add_custom_target(db_gen_sql_ddl
|
||||
DEPENDS "${_stamp_file}")
|
||||
add_dependencies(db_gen_sql_ddl db_gen_cpp_headers)
|
||||
|
||||
set_target_properties(db_gen_sql_ddl PROPERTIES EXCLUDE_FROM_ALL TRUE)
|
||||
endfunction()
|
||||
|
||||
@@ -12,6 +12,7 @@ function(cppbessot_add_db_gen_ts_target schema_dir)
|
||||
# - Files under `<schema_dir>/generated-ts-types`.
|
||||
cppbessot_validate_schema_dir_name("${schema_dir}")
|
||||
cppbessot_get_schema_dir_path(_version_dir "${schema_dir}")
|
||||
cppbessot_get_module_root(_module_root)
|
||||
|
||||
set(_openapi_file "${_version_dir}/openapi/openapi.yaml")
|
||||
set(_output_dir "${_version_dir}/generated-ts-types")
|
||||
@@ -22,6 +23,7 @@ function(cppbessot_add_db_gen_ts_target schema_dir)
|
||||
-i "${_openapi_file}"
|
||||
-g typescript-fetch
|
||||
-o "${_output_dir}"
|
||||
WORKING_DIRECTORY "${_module_root}"
|
||||
COMMENT "Generating TypeScript types for ${schema_dir}"
|
||||
VERBATIM
|
||||
)
|
||||
|
||||
@@ -20,7 +20,7 @@ function(cppbessot_add_db_gen_zod_target schema_dir)
|
||||
|
||||
add_custom_target(db_gen_zod
|
||||
COMMAND ${CMAKE_COMMAND} -E make_directory "${_output_dir}"
|
||||
COMMAND "${CPPBESSOT_NPX_EXECUTABLE}" --no-install openapi-zod-client
|
||||
COMMAND "${CPPBESSOT_NPX_EXECUTABLE}" --yes openapi-zod-client
|
||||
"${_openapi_file}"
|
||||
--output "${_output_file}"
|
||||
--export-schemas
|
||||
|
||||
+191
-24
@@ -41,6 +41,20 @@ function(cppbessot_initialize_paths)
|
||||
set(CPPBESSOT_WORKDIR_ABS "${CPPBESSOT_WORKDIR_ABS}" PARENT_SCOPE)
|
||||
endfunction()
|
||||
|
||||
function(cppbessot_get_module_root out_var)
|
||||
# Purpose: Resolve the CppBeSSOT module root, respecting parent overrides.
|
||||
# Inputs:
|
||||
# - CPPBESSOT_MODULE_ROOT (optional): Parent-provided module root path.
|
||||
# Outputs:
|
||||
# - <out_var> (PARENT_SCOPE): Absolute module root path.
|
||||
if(DEFINED CPPBESSOT_MODULE_ROOT AND NOT "${CPPBESSOT_MODULE_ROOT}" STREQUAL "")
|
||||
get_filename_component(_module_root "${CPPBESSOT_MODULE_ROOT}" ABSOLUTE)
|
||||
else()
|
||||
get_filename_component(_module_root "${_CPPBESSOT_GENERATION_COMMON_DIR}/.." ABSOLUTE)
|
||||
endif()
|
||||
set(${out_var} "${_module_root}" PARENT_SCOPE)
|
||||
endfunction()
|
||||
|
||||
function(cppbessot_require_var var_name)
|
||||
# Purpose: Fail fast if a required CMake variable is missing/empty.
|
||||
# Inputs:
|
||||
@@ -92,6 +106,19 @@ function(cppbessot_assert_schema_dir_exists schema_dir)
|
||||
endif()
|
||||
endfunction()
|
||||
|
||||
function(cppbessot_assert_openapi_exists schema_dir)
|
||||
# Purpose: Assert that a schema directory's SSOT OpenAPI file exists on disk.
|
||||
# Inputs:
|
||||
# - schema_dir: Schema directory basename.
|
||||
# Outputs:
|
||||
# - No return value; raises FATAL_ERROR if file is missing.
|
||||
cppbessot_get_schema_dir_path(_schema_dir_path "${schema_dir}")
|
||||
set(_openapi_file "${_schema_dir_path}/openapi/openapi.yaml")
|
||||
if(NOT EXISTS "${_openapi_file}")
|
||||
message(FATAL_ERROR "OpenAPI file does not exist: ${_openapi_file}")
|
||||
endif()
|
||||
endfunction()
|
||||
|
||||
function(cppbessot_get_model_headers_glob out_var schema_dir)
|
||||
# Purpose: Build a model-header glob expression for a schema directory.
|
||||
# Inputs:
|
||||
@@ -103,21 +130,22 @@ function(cppbessot_get_model_headers_glob out_var schema_dir)
|
||||
set(${out_var} "${_schema_dir_path}/generated-cpp-source/include/*/model/*.h" PARENT_SCOPE)
|
||||
endfunction()
|
||||
|
||||
function(cppbessot_get_openapi_schema_names out_var schema_dir)
|
||||
# Purpose: Parse top-level component schema names from a schema directory's OpenAPI file.
|
||||
function(_cppbessot_collect_openapi_schema_names out_var openapi_file filter_mode)
|
||||
# Purpose: Shared OpenAPI components.schemas walker.
|
||||
# Inputs:
|
||||
# - out_var: Parent-scope variable name to receive the schema names.
|
||||
# - schema_dir: Schema directory basename.
|
||||
# - out_var: Parent-scope variable for collected schema names.
|
||||
# - openapi_file: Absolute path to openapi.yaml.
|
||||
# - filter_mode: ALL (every top-level schema) or ODB_TABLE (x-odbTable only).
|
||||
# Outputs:
|
||||
# - <out_var> (PARENT_SCOPE): List of top-level component schema names.
|
||||
cppbessot_get_schema_dir_path(_schema_dir_path "${schema_dir}")
|
||||
set(_openapi_file "${_schema_dir_path}/openapi/openapi.yaml")
|
||||
if(NOT EXISTS "${_openapi_file}")
|
||||
message(FATAL_ERROR "OpenAPI file does not exist: ${_openapi_file}")
|
||||
# - <out_var> (PARENT_SCOPE): Collected schema names.
|
||||
if(NOT "${filter_mode}" STREQUAL "ALL" AND NOT "${filter_mode}" STREQUAL "ODB_TABLE")
|
||||
message(FATAL_ERROR
|
||||
"Unsupported OpenAPI schema filter `${filter_mode}`; expected ALL or ODB_TABLE.")
|
||||
endif()
|
||||
|
||||
file(STRINGS "${_openapi_file}" _openapi_lines)
|
||||
file(STRINGS "${openapi_file}" _openapi_lines)
|
||||
set(_schema_names)
|
||||
set(_current_schema_name)
|
||||
set(_in_components FALSE)
|
||||
set(_in_schemas FALSE)
|
||||
|
||||
@@ -125,8 +153,16 @@ function(cppbessot_get_openapi_schema_names out_var schema_dir)
|
||||
if(_in_schemas)
|
||||
if(_line MATCHES "^[^ ]" OR _line MATCHES "^ [^ ]")
|
||||
set(_in_schemas FALSE)
|
||||
unset(_current_schema_name)
|
||||
elseif(_line MATCHES "^ ([A-Za-z_][A-Za-z0-9_]*)[ \t]*:[ \t]*$")
|
||||
list(APPEND _schema_names "${CMAKE_MATCH_1}")
|
||||
set(_current_schema_name "${CMAKE_MATCH_1}")
|
||||
if("${filter_mode}" STREQUAL "ALL")
|
||||
list(APPEND _schema_names "${_current_schema_name}")
|
||||
endif()
|
||||
elseif("${filter_mode}" STREQUAL "ODB_TABLE"
|
||||
AND _current_schema_name
|
||||
AND _line MATCHES "^ x-odbTable:[ \t]*")
|
||||
list(APPEND _schema_names "${_current_schema_name}")
|
||||
endif()
|
||||
endif()
|
||||
|
||||
@@ -144,10 +180,52 @@ function(cppbessot_get_openapi_schema_names out_var schema_dir)
|
||||
endforeach()
|
||||
|
||||
if(NOT _schema_names)
|
||||
if("${filter_mode}" STREQUAL "ODB_TABLE")
|
||||
message(FATAL_ERROR
|
||||
"No component schema names were found in ${_openapi_file}.")
|
||||
"No x-odbTable schema names were found in ${openapi_file}.")
|
||||
else()
|
||||
message(FATAL_ERROR
|
||||
"No component schema names were found in ${openapi_file}.")
|
||||
endif()
|
||||
endif()
|
||||
|
||||
list(REMOVE_DUPLICATES _schema_names)
|
||||
set(${out_var} "${_schema_names}" PARENT_SCOPE)
|
||||
endfunction()
|
||||
|
||||
function(_cppbessot_get_openapi_file_path out_var schema_dir)
|
||||
# Purpose: Resolve and validate a schema directory's SSOT OpenAPI file path.
|
||||
# Inputs:
|
||||
# - out_var: Parent-scope variable name to receive the absolute OpenAPI path.
|
||||
# - schema_dir: Schema directory basename.
|
||||
# Outputs:
|
||||
# - <out_var> (PARENT_SCOPE): Absolute path to openapi.yaml.
|
||||
cppbessot_get_schema_dir_path(_schema_dir_path "${schema_dir}")
|
||||
cppbessot_assert_openapi_exists("${schema_dir}")
|
||||
set(${out_var} "${_schema_dir_path}/openapi/openapi.yaml" PARENT_SCOPE)
|
||||
endfunction()
|
||||
|
||||
function(cppbessot_get_openapi_schema_names out_var schema_dir)
|
||||
# Purpose: Parse top-level component schema names from a schema directory's OpenAPI file.
|
||||
# Inputs:
|
||||
# - out_var: Parent-scope variable name to receive the schema names.
|
||||
# - schema_dir: Schema directory basename.
|
||||
# Outputs:
|
||||
# - <out_var> (PARENT_SCOPE): List of top-level component schema names.
|
||||
_cppbessot_get_openapi_file_path(_openapi_file "${schema_dir}")
|
||||
_cppbessot_collect_openapi_schema_names(_schema_names "${_openapi_file}" "ALL")
|
||||
set(${out_var} "${_schema_names}" PARENT_SCOPE)
|
||||
endfunction()
|
||||
|
||||
function(cppbessot_get_openapi_odb_table_schema_names out_var schema_dir)
|
||||
# Purpose: Parse OpenAPI schema names that declare x-odbTable (ODB object models).
|
||||
# Inputs:
|
||||
# - out_var: Parent-scope variable name to receive the schema names.
|
||||
# - schema_dir: Schema directory basename.
|
||||
# Outputs:
|
||||
# - <out_var> (PARENT_SCOPE): Schema names with x-odbTable in the OpenAPI file.
|
||||
_cppbessot_get_openapi_file_path(_openapi_file "${schema_dir}")
|
||||
_cppbessot_collect_openapi_schema_names(_schema_names "${_openapi_file}" "ODB_TABLE")
|
||||
set(${out_var} "${_schema_names}" PARENT_SCOPE)
|
||||
endfunction()
|
||||
|
||||
@@ -176,8 +254,41 @@ function(cppbessot_get_expected_cpp_model_outputs out_headers_var out_sources_va
|
||||
set(${out_sources_var} "${_sources}" PARENT_SCOPE)
|
||||
endfunction()
|
||||
|
||||
function(_cppbessot_build_odb_table_artifact_paths
|
||||
schema_dir
|
||||
schema_names
|
||||
out_model_headers_var
|
||||
out_sqlite_sources_var
|
||||
out_pgsql_sources_var)
|
||||
# Purpose: Build expected ODB model-header and backend source paths from schema names.
|
||||
# Inputs:
|
||||
# - schema_dir: Schema directory basename.
|
||||
# - schema_names: x-odbTable schema names.
|
||||
# Outputs:
|
||||
# - <out_model_headers_var> (PARENT_SCOPE): Expected generated ODB model headers.
|
||||
# - <out_sqlite_sources_var> (PARENT_SCOPE): Expected sqlite `*-odb.cxx` sources.
|
||||
# - <out_pgsql_sources_var> (PARENT_SCOPE): Expected postgre `*-odb.cxx` sources.
|
||||
cppbessot_get_schema_dir_path(_schema_dir_path "${schema_dir}")
|
||||
|
||||
set(_headers)
|
||||
set(_sqlite_sources)
|
||||
set(_pgsql_sources)
|
||||
foreach(_schema_name IN LISTS schema_names)
|
||||
list(APPEND _headers
|
||||
"${_schema_dir_path}/generated-cpp-source/include/cppbessot/model/${_schema_name}.h")
|
||||
list(APPEND _sqlite_sources
|
||||
"${_schema_dir_path}/generated-odb-source/sqlite/${_schema_name}-odb.cxx")
|
||||
list(APPEND _pgsql_sources
|
||||
"${_schema_dir_path}/generated-odb-source/postgre/${_schema_name}-odb.cxx")
|
||||
endforeach()
|
||||
|
||||
set(${out_model_headers_var} "${_headers}" PARENT_SCOPE)
|
||||
set(${out_sqlite_sources_var} "${_sqlite_sources}" PARENT_SCOPE)
|
||||
set(${out_pgsql_sources_var} "${_pgsql_sources}" PARENT_SCOPE)
|
||||
endfunction()
|
||||
|
||||
function(cppbessot_get_expected_odb_outputs out_sqlite_sources_var out_pgsql_sources_var schema_dir)
|
||||
# Purpose: Infer generated ODB backend source files from OpenAPI schema names.
|
||||
# Purpose: Infer generated ODB backend source files from x-odbTable OpenAPI schemas.
|
||||
# Inputs:
|
||||
# - out_sqlite_sources_var: Parent-scope variable for sqlite `*-odb.cxx`.
|
||||
# - out_pgsql_sources_var: Parent-scope variable for postgre `*-odb.cxx`.
|
||||
@@ -185,18 +296,74 @@ function(cppbessot_get_expected_odb_outputs out_sqlite_sources_var out_pgsql_sou
|
||||
# Outputs:
|
||||
# - <out_sqlite_sources_var> (PARENT_SCOPE): Expected sqlite ODB sources.
|
||||
# - <out_pgsql_sources_var> (PARENT_SCOPE): Expected postgre ODB sources.
|
||||
cppbessot_get_schema_dir_path(_schema_dir_path "${schema_dir}")
|
||||
cppbessot_get_openapi_schema_names(_schema_names "${schema_dir}")
|
||||
|
||||
set(_sqlite_sources)
|
||||
set(_pgsql_sources)
|
||||
foreach(_schema_name IN LISTS _schema_names)
|
||||
list(APPEND _sqlite_sources
|
||||
"${_schema_dir_path}/generated-odb-source/sqlite/${_schema_name}-odb.cxx")
|
||||
list(APPEND _pgsql_sources
|
||||
"${_schema_dir_path}/generated-odb-source/postgre/${_schema_name}-odb.cxx")
|
||||
endforeach()
|
||||
cppbessot_get_openapi_odb_table_schema_names(_schema_names "${schema_dir}")
|
||||
_cppbessot_build_odb_table_artifact_paths(
|
||||
"${schema_dir}"
|
||||
"${_schema_names}"
|
||||
_expected_model_headers
|
||||
_sqlite_sources
|
||||
_pgsql_sources)
|
||||
|
||||
set(${out_sqlite_sources_var} "${_sqlite_sources}" PARENT_SCOPE)
|
||||
set(${out_pgsql_sources_var} "${_pgsql_sources}" PARENT_SCOPE)
|
||||
endfunction()
|
||||
|
||||
function(cppbessot_get_expected_odb_model_headers out_var schema_dir)
|
||||
# Purpose: Infer generated C++ model headers for x-odbTable OpenAPI schemas.
|
||||
# Inputs:
|
||||
# - out_var: Parent-scope variable name to receive expected ODB model headers.
|
||||
# - schema_dir: Schema directory basename.
|
||||
# Outputs:
|
||||
# - <out_var> (PARENT_SCOPE): Expected generated model headers for ODB objects.
|
||||
cppbessot_get_openapi_odb_table_schema_names(_schema_names "${schema_dir}")
|
||||
_cppbessot_build_odb_table_artifact_paths(
|
||||
"${schema_dir}"
|
||||
"${_schema_names}"
|
||||
_headers
|
||||
_sqlite_sources
|
||||
_pgsql_sources)
|
||||
|
||||
set(${out_var} "${_headers}" PARENT_SCOPE)
|
||||
endfunction()
|
||||
|
||||
function(cppbessot_get_expected_odb_outputs_all out_var schema_dir)
|
||||
# Purpose: Infer all generated ODB backend `*-odb.cxx` files for both backends.
|
||||
# Inputs:
|
||||
# - out_var: Parent-scope variable name to receive expected ODB sources.
|
||||
# - schema_dir: Schema directory basename.
|
||||
# Outputs:
|
||||
# - <out_var> (PARENT_SCOPE): Expected sqlite + postgre ODB sources.
|
||||
cppbessot_get_expected_odb_outputs(
|
||||
_expected_sqlite_odb_sources
|
||||
_expected_pgsql_odb_sources
|
||||
"${schema_dir}")
|
||||
set(_combined
|
||||
${_expected_sqlite_odb_sources}
|
||||
${_expected_pgsql_odb_sources})
|
||||
set(${out_var} "${_combined}" PARENT_SCOPE)
|
||||
endfunction()
|
||||
|
||||
function(cppbessot_get_expected_odb_generation_artifacts
|
||||
out_model_headers_var
|
||||
out_all_sources_var
|
||||
schema_dir)
|
||||
# Purpose: Infer ODB generation dependencies and outputs in a single OpenAPI pass.
|
||||
# Inputs:
|
||||
# - out_model_headers_var: Parent-scope variable for expected ODB model headers.
|
||||
# - out_all_sources_var: Parent-scope variable for expected sqlite + postgre sources.
|
||||
# - schema_dir: Schema directory basename.
|
||||
# Outputs:
|
||||
# - <out_model_headers_var> (PARENT_SCOPE): Expected generated ODB model headers.
|
||||
# - <out_all_sources_var> (PARENT_SCOPE): Expected sqlite + postgre `*-odb.cxx` sources.
|
||||
cppbessot_get_openapi_odb_table_schema_names(_schema_names "${schema_dir}")
|
||||
_cppbessot_build_odb_table_artifact_paths(
|
||||
"${schema_dir}"
|
||||
"${_schema_names}"
|
||||
_headers
|
||||
_sqlite_sources
|
||||
_pgsql_sources)
|
||||
set(_combined ${_sqlite_sources} ${_pgsql_sources})
|
||||
|
||||
set(${out_model_headers_var} "${_headers}" PARENT_SCOPE)
|
||||
set(${out_all_sources_var} "${_combined}" PARENT_SCOPE)
|
||||
endfunction()
|
||||
|
||||
@@ -0,0 +1,41 @@
|
||||
include_guard(GLOBAL)
|
||||
|
||||
function(_cppbessot_dotenv_escape output_var value)
|
||||
set(escaped "${value}")
|
||||
string(REPLACE "\\" "\\\\" escaped "${escaped}")
|
||||
string(REPLACE "\"" "\\\"" escaped "${escaped}")
|
||||
string(REPLACE "\n" "\\n" escaped "${escaped}")
|
||||
string(REPLACE "\r" "\\r" escaped "${escaped}")
|
||||
string(REPLACE "\t" "\\t" escaped "${escaped}")
|
||||
set(${output_var} "${escaped}" PARENT_SCOPE)
|
||||
endfunction()
|
||||
|
||||
function(_cppbessot_dotenv_append output_var name value)
|
||||
_cppbessot_dotenv_escape(escaped_value "${value}")
|
||||
set(line "${name}=\"${escaped_value}\"\n")
|
||||
set(${output_var} "${${output_var}}${line}" PARENT_SCOPE)
|
||||
endfunction()
|
||||
|
||||
function(cppbessot_write_runtime_env_file output_path)
|
||||
set(env_contents
|
||||
"# Generated by cppbessot during CMake configure. Do not edit.\n")
|
||||
_cppbessot_dotenv_append(env_contents
|
||||
"DB_TARGET" "${DB_TARGET}")
|
||||
_cppbessot_dotenv_append(env_contents
|
||||
"CPPBESSOT_DB_PGSQL_PROD_CONNSTR" "${CPPBESSOT_DB_PGSQL_PROD_CONNSTR}")
|
||||
_cppbessot_dotenv_append(env_contents
|
||||
"CPPBESSOT_DB_PGSQL_DEV_CONNSTR" "${CPPBESSOT_DB_PGSQL_DEV_CONNSTR}")
|
||||
_cppbessot_dotenv_append(env_contents
|
||||
"CPPBESSOT_DB_PGSQL_PRODDEV_CONNSTR" "${CPPBESSOT_DB_PGSQL_PRODDEV_CONNSTR}")
|
||||
_cppbessot_dotenv_append(env_contents
|
||||
"CPPBESSOT_DB_PGSQL_TESTS_CONNSTR" "${CPPBESSOT_DB_PGSQL_TESTS_CONNSTR}")
|
||||
_cppbessot_dotenv_append(env_contents
|
||||
"CPPBESSOT_DB_SQLITE_PROD_PATH" "${CPPBESSOT_DB_SQLITE_PROD_PATH}")
|
||||
_cppbessot_dotenv_append(env_contents
|
||||
"CPPBESSOT_DB_SQLITE_DEV_PATH" "${CPPBESSOT_DB_SQLITE_DEV_PATH}")
|
||||
_cppbessot_dotenv_append(env_contents
|
||||
"CPPBESSOT_DB_SQLITE_PRODDEV_PATH" "${CPPBESSOT_DB_SQLITE_PRODDEV_PATH}")
|
||||
_cppbessot_dotenv_append(env_contents
|
||||
"CPPBESSOT_DB_SQLITE_TESTS_PATH" "${CPPBESSOT_DB_SQLITE_TESTS_PATH}")
|
||||
file(WRITE "${output_path}" "${env_contents}")
|
||||
endfunction()
|
||||
@@ -0,0 +1,48 @@
|
||||
cmake_minimum_required(VERSION 3.16)
|
||||
|
||||
function(cppbessot_db_action_get_hook_path out_var migration_dir hook_name)
|
||||
set(_hook_path "${migration_dir}/${hook_name}")
|
||||
if(EXISTS "${_hook_path}")
|
||||
set(${out_var} "${_hook_path}" PARENT_SCOPE)
|
||||
else()
|
||||
set(${out_var} "" PARENT_SCOPE)
|
||||
endif()
|
||||
endfunction()
|
||||
|
||||
function(cppbessot_db_action_run_hook
|
||||
hook_path
|
||||
db_target
|
||||
backend
|
||||
migration_dir
|
||||
migrate_with
|
||||
schema_dir_to_generate
|
||||
createfrom_schema_dir
|
||||
sqlite_path
|
||||
pgsql_connstr)
|
||||
if("${hook_path}" STREQUAL "")
|
||||
return()
|
||||
endif()
|
||||
|
||||
set(_env_args
|
||||
"CPPBESSOT_DB_TARGET=${db_target}"
|
||||
"CPPBESSOT_DB_BACKEND=${backend}"
|
||||
"CPPBESSOT_DB_MIGRATION_DIR=${migration_dir}"
|
||||
"CPPBESSOT_DB_MIGRATE_WITH=${migrate_with}"
|
||||
"CPPBESSOT_DB_SCHEMA_DIR_TO_GENERATE=${schema_dir_to_generate}"
|
||||
"CPPBESSOT_DB_CREATEFROM_SCHEMA_DIR=${createfrom_schema_dir}"
|
||||
"CPPBESSOT_DB_SQLITE_PATH=${sqlite_path}"
|
||||
"CPPBESSOT_DB_PGSQL_CONNSTR=${pgsql_connstr}")
|
||||
|
||||
execute_process(
|
||||
COMMAND "${CMAKE_COMMAND}" -E env ${_env_args} sh "${hook_path}"
|
||||
WORKING_DIRECTORY "${migration_dir}"
|
||||
RESULT_VARIABLE _result
|
||||
OUTPUT_VARIABLE _stdout
|
||||
ERROR_VARIABLE _stderr
|
||||
)
|
||||
|
||||
if(NOT _result EQUAL 0)
|
||||
message(FATAL_ERROR
|
||||
"Backfill hook failed: ${hook_path}\n${_stdout}\n${_stderr}")
|
||||
endif()
|
||||
endfunction()
|
||||
@@ -0,0 +1,77 @@
|
||||
cmake_minimum_required(VERSION 3.16)
|
||||
|
||||
function(cppbessot_db_action_target_exists out_var backend sqlite_path pgsql_connstr)
|
||||
if("${backend}" STREQUAL "sqlite")
|
||||
if(EXISTS "${sqlite_path}")
|
||||
set(${out_var} TRUE PARENT_SCOPE)
|
||||
else()
|
||||
set(${out_var} FALSE PARENT_SCOPE)
|
||||
endif()
|
||||
return()
|
||||
endif()
|
||||
|
||||
cppbessot_db_action_find_program_or_fail(_psql psql
|
||||
"PostgreSQL live actions require `psql` to be available in PATH.")
|
||||
execute_process(
|
||||
COMMAND "${_psql}" "${pgsql_connstr}" -v ON_ERROR_STOP=1 -c "SELECT 1;"
|
||||
RESULT_VARIABLE _result
|
||||
OUTPUT_QUIET
|
||||
ERROR_QUIET
|
||||
)
|
||||
if(_result EQUAL 0)
|
||||
set(${out_var} TRUE PARENT_SCOPE)
|
||||
else()
|
||||
set(${out_var} FALSE PARENT_SCOPE)
|
||||
endif()
|
||||
endfunction()
|
||||
|
||||
function(cppbessot_db_action_get_clone_command out_var backend target)
|
||||
if("${backend}" STREQUAL "sqlite")
|
||||
set(_command "${CPPBESSOT_DB_SQLITE_CLONE_PROD_TO_PRODDEV_COMMAND}")
|
||||
else()
|
||||
set(_command "${CPPBESSOT_DB_PGSQL_CLONE_PROD_TO_PRODDEV_COMMAND}")
|
||||
endif()
|
||||
set(${out_var} "${_command}" PARENT_SCOPE)
|
||||
endfunction()
|
||||
|
||||
function(cppbessot_db_action_invoke_clone_hook backend target)
|
||||
cppbessot_db_action_get_clone_command(_clone_command "${backend}" "${target}")
|
||||
if("${_clone_command}" STREQUAL "")
|
||||
message(FATAL_ERROR
|
||||
"No clone command is configured for backend `${backend}` while preparing target `${target}`.")
|
||||
endif()
|
||||
|
||||
execute_process(
|
||||
COMMAND sh -c "${_clone_command}"
|
||||
RESULT_VARIABLE _result
|
||||
OUTPUT_VARIABLE _stdout
|
||||
ERROR_VARIABLE _stderr
|
||||
)
|
||||
|
||||
if(NOT _result EQUAL 0)
|
||||
message(FATAL_ERROR
|
||||
"Clone command failed for backend `${backend}` target `${target}`.\n${_stdout}\n${_stderr}")
|
||||
endif()
|
||||
endfunction()
|
||||
|
||||
function(cppbessot_db_action_prepare_proddev target backend use_stale sqlite_path pgsql_connstr)
|
||||
if(NOT "${target}" STREQUAL "proddev")
|
||||
return()
|
||||
endif()
|
||||
|
||||
if(use_stale)
|
||||
cppbessot_db_action_target_exists(_exists "${backend}" "${sqlite_path}" "${pgsql_connstr}")
|
||||
if(NOT _exists)
|
||||
message(FATAL_ERROR
|
||||
"DB_MIGRATE_PRODDEV_USE_STALE is ON, but no current stale proddev target exists.")
|
||||
endif()
|
||||
return()
|
||||
endif()
|
||||
|
||||
cppbessot_db_action_invoke_clone_hook("${backend}" "${target}")
|
||||
cppbessot_db_action_target_exists(_exists "${backend}" "${sqlite_path}" "${pgsql_connstr}")
|
||||
if(NOT _exists)
|
||||
message(FATAL_ERROR
|
||||
"Proddev clone command completed, but the proddev target still does not appear to exist.")
|
||||
endif()
|
||||
endfunction()
|
||||
@@ -0,0 +1,168 @@
|
||||
cmake_minimum_required(VERSION 3.16)
|
||||
|
||||
include("${CMAKE_CURRENT_LIST_DIR}/../dbActionShared.cmake")
|
||||
|
||||
function(cppbessot_db_action_require_var var_name)
|
||||
if(NOT DEFINED ${var_name} OR "${${var_name}}" STREQUAL "")
|
||||
message(FATAL_ERROR "Required variable `${var_name}` is missing.")
|
||||
endif()
|
||||
endfunction()
|
||||
|
||||
function(cppbessot_db_action_resolve_project_path out_var input_path)
|
||||
cppbessot_db_action_require_var(CPPBESSOT_PROJECT_SOURCE_DIR)
|
||||
if(IS_ABSOLUTE "${input_path}")
|
||||
set(_resolved "${input_path}")
|
||||
else()
|
||||
set(_resolved "${CPPBESSOT_PROJECT_SOURCE_DIR}/${input_path}")
|
||||
endif()
|
||||
|
||||
get_filename_component(_resolved "${_resolved}" ABSOLUTE)
|
||||
set(${out_var} "${_resolved}" PARENT_SCOPE)
|
||||
endfunction()
|
||||
|
||||
function(cppbessot_db_action_validate_schema_dir_name schema_dir)
|
||||
_cppbessot_db_action_validate_basename(
|
||||
"${schema_dir}"
|
||||
"Schema directory name"
|
||||
"CPPBESSOT_WORKDIR")
|
||||
endfunction()
|
||||
|
||||
function(cppbessot_db_action_get_schema_dir_path out_var schema_dir)
|
||||
cppbessot_db_action_validate_schema_dir_name("${schema_dir}")
|
||||
cppbessot_db_action_resolve_project_path(_workdir "${CPPBESSOT_WORKDIR}")
|
||||
set(${out_var} "${_workdir}/${schema_dir}" PARENT_SCOPE)
|
||||
endfunction()
|
||||
|
||||
function(cppbessot_db_action_assert_schema_dir_ready schema_dir)
|
||||
cppbessot_db_action_get_schema_dir_path(_schema_dir_path "${schema_dir}")
|
||||
if(NOT IS_DIRECTORY "${_schema_dir_path}")
|
||||
message(FATAL_ERROR "Schema directory does not exist: ${_schema_dir_path}")
|
||||
endif()
|
||||
|
||||
set(_openapi_file "${_schema_dir_path}/openapi/openapi.yaml")
|
||||
if(NOT EXISTS "${_openapi_file}")
|
||||
message(FATAL_ERROR "OpenAPI file does not exist: ${_openapi_file}")
|
||||
endif()
|
||||
endfunction()
|
||||
|
||||
function(cppbessot_db_action_validate_migration_dir_name migration_dir)
|
||||
_cppbessot_db_action_validate_basename(
|
||||
"${migration_dir}"
|
||||
"Migration directory name"
|
||||
"CPPBESSOT_WORKDIR/migrations")
|
||||
endfunction()
|
||||
|
||||
function(cppbessot_db_action_get_migration_dir_path out_var migration_dir)
|
||||
cppbessot_db_action_validate_migration_dir_name("${migration_dir}")
|
||||
cppbessot_db_action_resolve_project_path(_workdir "${CPPBESSOT_WORKDIR}")
|
||||
set(${out_var} "${_workdir}/migrations/${migration_dir}" PARENT_SCOPE)
|
||||
endfunction()
|
||||
|
||||
function(cppbessot_db_action_assert_migration_dir_exists migration_dir)
|
||||
cppbessot_db_action_get_migration_dir_path(_migration_dir_path "${migration_dir}")
|
||||
if(NOT IS_DIRECTORY "${_migration_dir_path}")
|
||||
message(FATAL_ERROR "Migration directory does not exist: ${_migration_dir_path}")
|
||||
endif()
|
||||
endfunction()
|
||||
|
||||
function(cppbessot_db_action_validate_db_target db_target)
|
||||
_cppbessot_db_action_validate_db_target_impl("${db_target}")
|
||||
endfunction()
|
||||
|
||||
function(cppbessot_db_action_backend_subdir out_var backend)
|
||||
if("${backend}" STREQUAL "sqlite")
|
||||
set(${out_var} "sqlite" PARENT_SCOPE)
|
||||
return()
|
||||
endif()
|
||||
|
||||
if("${backend}" STREQUAL "postgre")
|
||||
set(${out_var} "postgre" PARENT_SCOPE)
|
||||
return()
|
||||
endif()
|
||||
|
||||
message(FATAL_ERROR "Unsupported backend `${backend}`.")
|
||||
endfunction()
|
||||
|
||||
function(cppbessot_db_action_collect_nonempty_sql_files out_var sql_dir)
|
||||
if(NOT IS_DIRECTORY "${sql_dir}")
|
||||
set(${out_var} "" PARENT_SCOPE)
|
||||
return()
|
||||
endif()
|
||||
|
||||
file(GLOB _candidate_files "${sql_dir}/*.sql")
|
||||
list(SORT _candidate_files)
|
||||
|
||||
set(_sql_files)
|
||||
foreach(_candidate IN LISTS _candidate_files)
|
||||
file(READ "${_candidate}" _contents)
|
||||
string(STRIP "${_contents}" _trimmed)
|
||||
if(NOT "${_trimmed}" STREQUAL "")
|
||||
list(APPEND _sql_files "${_candidate}")
|
||||
endif()
|
||||
endforeach()
|
||||
|
||||
set(${out_var} "${_sql_files}" PARENT_SCOPE)
|
||||
endfunction()
|
||||
|
||||
function(cppbessot_db_action_is_migration_sql_file out_var sql_file)
|
||||
get_filename_component(_filename "${sql_file}" NAME)
|
||||
if("${_filename}" MATCHES "-[0-9]+-(pre|post)\\.sql$")
|
||||
set(${out_var} TRUE PARENT_SCOPE)
|
||||
return()
|
||||
endif()
|
||||
set(${out_var} FALSE PARENT_SCOPE)
|
||||
endfunction()
|
||||
|
||||
function(cppbessot_db_action_collect_schema_sql_files out_var sql_dir)
|
||||
cppbessot_db_action_collect_nonempty_sql_files(_candidate_files "${sql_dir}")
|
||||
set(_sql_files)
|
||||
foreach(_candidate IN LISTS _candidate_files)
|
||||
cppbessot_db_action_is_migration_sql_file(_is_migration_sql "${_candidate}")
|
||||
if(NOT _is_migration_sql)
|
||||
list(APPEND _sql_files "${_candidate}")
|
||||
endif()
|
||||
endforeach()
|
||||
set(${out_var} "${_sql_files}" PARENT_SCOPE)
|
||||
endfunction()
|
||||
|
||||
function(cppbessot_db_action_migration_sort_key out_var sql_file)
|
||||
get_filename_component(_filename "${sql_file}" NAME)
|
||||
string(REGEX REPLACE "-([0-9]+)-pre\\.sql$" "-\\1-0-pre.sql" _sort_key "${_filename}")
|
||||
string(REGEX REPLACE "-([0-9]+)-post\\.sql$" "-\\1-1-post.sql" _sort_key "${_sort_key}")
|
||||
set(${out_var} "${_sort_key}" PARENT_SCOPE)
|
||||
endfunction()
|
||||
|
||||
function(cppbessot_db_action_collect_migration_sql_files out_var sql_dir)
|
||||
cppbessot_db_action_collect_nonempty_sql_files(_candidate_files "${sql_dir}")
|
||||
set(_keyed_files)
|
||||
foreach(_candidate IN LISTS _candidate_files)
|
||||
cppbessot_db_action_is_migration_sql_file(_is_migration_sql "${_candidate}")
|
||||
if(_is_migration_sql)
|
||||
cppbessot_db_action_migration_sort_key(_sort_key "${_candidate}")
|
||||
list(APPEND _keyed_files "${_sort_key}|${_candidate}")
|
||||
endif()
|
||||
endforeach()
|
||||
|
||||
list(SORT _keyed_files)
|
||||
set(_sql_files)
|
||||
foreach(_keyed_file IN LISTS _keyed_files)
|
||||
string(REGEX REPLACE "^[^|]+\\|" "" _sql_file "${_keyed_file}")
|
||||
list(APPEND _sql_files "${_sql_file}")
|
||||
endforeach()
|
||||
set(${out_var} "${_sql_files}" PARENT_SCOPE)
|
||||
endfunction()
|
||||
|
||||
function(cppbessot_db_action_require_nonempty_sql_dir sql_dir failure_prefix)
|
||||
cppbessot_db_action_collect_schema_sql_files(_sql_files "${sql_dir}")
|
||||
if(NOT _sql_files)
|
||||
message(FATAL_ERROR "${failure_prefix}: no non-empty SQL files found under ${sql_dir}")
|
||||
endif()
|
||||
endfunction()
|
||||
|
||||
function(cppbessot_db_action_find_program_or_fail out_var program_name hint)
|
||||
find_program(_program "${program_name}")
|
||||
if(NOT _program)
|
||||
message(FATAL_ERROR "Missing required program `${program_name}`. ${hint}")
|
||||
endif()
|
||||
set(${out_var} "${_program}" PARENT_SCOPE)
|
||||
endfunction()
|
||||
@@ -0,0 +1,78 @@
|
||||
cmake_minimum_required(VERSION 3.16)
|
||||
|
||||
function(cppbessot_db_action_reset_sqlite_db sqlite_path)
|
||||
if(EXISTS "${sqlite_path}")
|
||||
file(REMOVE "${sqlite_path}")
|
||||
endif()
|
||||
|
||||
get_filename_component(_sqlite_parent "${sqlite_path}" DIRECTORY)
|
||||
if(NOT "${_sqlite_parent}" STREQUAL "")
|
||||
file(MAKE_DIRECTORY "${_sqlite_parent}")
|
||||
endif()
|
||||
endfunction()
|
||||
|
||||
function(cppbessot_db_action_apply_sqlite_files sqlite_path)
|
||||
set(_sql_files "${ARGN}")
|
||||
if(NOT _sql_files)
|
||||
return()
|
||||
endif()
|
||||
|
||||
cppbessot_db_action_find_program_or_fail(_sqlite3 sqlite3
|
||||
"SQLite live actions require the `sqlite3` CLI to be available in PATH.")
|
||||
|
||||
foreach(_sql_file IN LISTS _sql_files)
|
||||
string(REPLACE "\"" "\\\"" _sqlite_read_file "${_sql_file}")
|
||||
execute_process(
|
||||
COMMAND "${_sqlite3}" "${sqlite_path}" ".read \"${_sqlite_read_file}\""
|
||||
RESULT_VARIABLE _result
|
||||
OUTPUT_VARIABLE _stdout
|
||||
ERROR_VARIABLE _stderr
|
||||
)
|
||||
|
||||
if(NOT _result EQUAL 0)
|
||||
message(FATAL_ERROR
|
||||
"SQLite SQL apply failed for `${_sql_file}` against `${sqlite_path}`.\n${_stdout}\n${_stderr}")
|
||||
endif()
|
||||
endforeach()
|
||||
endfunction()
|
||||
|
||||
function(cppbessot_db_action_reset_pgsql_schema pgsql_connstr)
|
||||
cppbessot_db_action_find_program_or_fail(_psql psql
|
||||
"PostgreSQL live actions require `psql` to be available in PATH.")
|
||||
execute_process(
|
||||
COMMAND "${_psql}" "${pgsql_connstr}" -v ON_ERROR_STOP=1
|
||||
-c "DROP SCHEMA IF EXISTS public CASCADE; CREATE SCHEMA public;"
|
||||
RESULT_VARIABLE _result
|
||||
OUTPUT_VARIABLE _stdout
|
||||
ERROR_VARIABLE _stderr
|
||||
)
|
||||
|
||||
if(NOT _result EQUAL 0)
|
||||
message(FATAL_ERROR
|
||||
"PostgreSQL schema reset failed.\n${_stdout}\n${_stderr}")
|
||||
endif()
|
||||
endfunction()
|
||||
|
||||
function(cppbessot_db_action_apply_pgsql_files pgsql_connstr)
|
||||
set(_sql_files "${ARGN}")
|
||||
if(NOT _sql_files)
|
||||
return()
|
||||
endif()
|
||||
|
||||
cppbessot_db_action_find_program_or_fail(_psql psql
|
||||
"PostgreSQL live actions require `psql` to be available in PATH.")
|
||||
|
||||
foreach(_sql_file IN LISTS _sql_files)
|
||||
execute_process(
|
||||
COMMAND "${_psql}" "${pgsql_connstr}" -v ON_ERROR_STOP=1 -f "${_sql_file}"
|
||||
RESULT_VARIABLE _result
|
||||
OUTPUT_VARIABLE _stdout
|
||||
ERROR_VARIABLE _stderr
|
||||
)
|
||||
|
||||
if(NOT _result EQUAL 0)
|
||||
message(FATAL_ERROR
|
||||
"PostgreSQL SQL apply failed for `${_sql_file}`.\n${_stdout}\n${_stderr}")
|
||||
endif()
|
||||
endforeach()
|
||||
endfunction()
|
||||
@@ -0,0 +1,50 @@
|
||||
cmake_minimum_required(VERSION 3.16)
|
||||
|
||||
function(cppbessot_db_action_target_upper out_var db_target)
|
||||
string(TOUPPER "${db_target}" _upper)
|
||||
set(${out_var} "${_upper}" PARENT_SCOPE)
|
||||
endfunction()
|
||||
|
||||
function(cppbessot_db_action_resolve_backend_for_target
|
||||
out_backend
|
||||
out_sqlite_path
|
||||
out_pgsql_connstr
|
||||
db_target)
|
||||
cppbessot_db_action_validate_db_target("${db_target}")
|
||||
cppbessot_db_action_target_upper(_target_upper "${db_target}")
|
||||
|
||||
set(_sqlite_var "CPPBESSOT_DB_SQLITE_${_target_upper}_PATH")
|
||||
set(_pgsql_var "CPPBESSOT_DB_PGSQL_${_target_upper}_CONNSTR")
|
||||
|
||||
set(_has_sqlite FALSE)
|
||||
set(_has_pgsql FALSE)
|
||||
|
||||
if(DEFINED ${_sqlite_var} AND NOT "${${_sqlite_var}}" STREQUAL "")
|
||||
set(_has_sqlite TRUE)
|
||||
endif()
|
||||
|
||||
if(DEFINED ${_pgsql_var} AND NOT "${${_pgsql_var}}" STREQUAL "")
|
||||
set(_has_pgsql TRUE)
|
||||
endif()
|
||||
|
||||
if(_has_sqlite AND _has_pgsql)
|
||||
message(FATAL_ERROR
|
||||
"DB target `${db_target}` is ambiguous: both `${_sqlite_var}` and `${_pgsql_var}` are set.")
|
||||
endif()
|
||||
|
||||
if(NOT _has_sqlite AND NOT _has_pgsql)
|
||||
message(FATAL_ERROR
|
||||
"DB target `${db_target}` is not mapped: set exactly one of `${_sqlite_var}` or `${_pgsql_var}`.")
|
||||
endif()
|
||||
|
||||
if(_has_sqlite)
|
||||
set(${out_backend} "sqlite" PARENT_SCOPE)
|
||||
set(${out_sqlite_path} "${${_sqlite_var}}" PARENT_SCOPE)
|
||||
set(${out_pgsql_connstr} "" PARENT_SCOPE)
|
||||
return()
|
||||
endif()
|
||||
|
||||
set(${out_backend} "postgre" PARENT_SCOPE)
|
||||
set(${out_sqlite_path} "" PARENT_SCOPE)
|
||||
set(${out_pgsql_connstr} "${${_pgsql_var}}" PARENT_SCOPE)
|
||||
endfunction()
|
||||
@@ -0,0 +1,20 @@
|
||||
function(cppbessot_odb_find_object_headers out_var include_dir)
|
||||
file(GLOB _headers "${include_dir}/*/model/*.h")
|
||||
if(NOT _headers)
|
||||
message(FATAL_ERROR "No model headers found under ${include_dir}")
|
||||
endif()
|
||||
|
||||
set(_object_headers "")
|
||||
foreach(_header IN LISTS _headers)
|
||||
file(READ "${_header}" _header_contents)
|
||||
if(_header_contents MATCHES "#pragma db object")
|
||||
list(APPEND _object_headers "${_header}")
|
||||
endif()
|
||||
endforeach()
|
||||
|
||||
if(NOT _object_headers)
|
||||
message(FATAL_ERROR "No ODB object headers found under ${include_dir}")
|
||||
endif()
|
||||
|
||||
set(${out_var} "${_object_headers}" PARENT_SCOPE)
|
||||
endfunction()
|
||||
@@ -0,0 +1,38 @@
|
||||
cmake_minimum_required(VERSION 3.16)
|
||||
|
||||
include("${CMAKE_CURRENT_LIST_DIR}/cppbessotDbActionCommon.cmake")
|
||||
include("${CMAKE_CURRENT_LIST_DIR}/cppbessotDbActionTargetResolution.cmake")
|
||||
include("${CMAKE_CURRENT_LIST_DIR}/cppbessotDbActionSqlApply.cmake")
|
||||
|
||||
cppbessot_db_action_require_var(CPPBESSOT_PROJECT_SOURCE_DIR)
|
||||
cppbessot_db_action_require_var(CPPBESSOT_WORKDIR)
|
||||
cppbessot_db_action_require_var(DB_CREATEFROM_SCHEMA_DIR)
|
||||
cppbessot_db_action_require_var(DB_TARGET)
|
||||
|
||||
cppbessot_db_action_validate_db_target("${DB_TARGET}")
|
||||
if("${DB_TARGET}" STREQUAL "proddev")
|
||||
message(FATAL_ERROR "db_createfrom does not support DB_TARGET=proddev.")
|
||||
endif()
|
||||
|
||||
cppbessot_db_action_assert_schema_dir_ready("${DB_CREATEFROM_SCHEMA_DIR}")
|
||||
cppbessot_db_action_resolve_backend_for_target(
|
||||
_backend
|
||||
_sqlite_path
|
||||
_pgsql_connstr
|
||||
"${DB_TARGET}")
|
||||
cppbessot_db_action_backend_subdir(_backend_subdir "${_backend}")
|
||||
cppbessot_db_action_get_schema_dir_path(_schema_dir "${DB_CREATEFROM_SCHEMA_DIR}")
|
||||
set(_ddl_dir "${_schema_dir}/generated-sql-ddl/${_backend_subdir}")
|
||||
cppbessot_db_action_require_nonempty_sql_dir(
|
||||
"${_ddl_dir}"
|
||||
"db_createfrom cannot continue")
|
||||
cppbessot_db_action_collect_schema_sql_files(_sql_files "${_ddl_dir}")
|
||||
|
||||
if("${_backend}" STREQUAL "sqlite")
|
||||
cppbessot_db_action_reset_sqlite_db("${_sqlite_path}")
|
||||
cppbessot_db_action_apply_sqlite_files("${_sqlite_path}" ${_sql_files})
|
||||
return()
|
||||
endif()
|
||||
|
||||
cppbessot_db_action_reset_pgsql_schema("${_pgsql_connstr}")
|
||||
cppbessot_db_action_apply_pgsql_files("${_pgsql_connstr}" ${_sql_files})
|
||||
@@ -0,0 +1,64 @@
|
||||
cmake_minimum_required(VERSION 3.16)
|
||||
|
||||
include("${CMAKE_CURRENT_LIST_DIR}/cppbessotDbActionCommon.cmake")
|
||||
include("${CMAKE_CURRENT_LIST_DIR}/cppbessotDbActionTargetResolution.cmake")
|
||||
include("${CMAKE_CURRENT_LIST_DIR}/cppbessotDbActionBackfill.cmake")
|
||||
include("${CMAKE_CURRENT_LIST_DIR}/cppbessotDbActionSqlApply.cmake")
|
||||
include("${CMAKE_CURRENT_LIST_DIR}/cppbessotDbActionClone.cmake")
|
||||
|
||||
cppbessot_db_action_require_var(CPPBESSOT_PROJECT_SOURCE_DIR)
|
||||
cppbessot_db_action_require_var(CPPBESSOT_WORKDIR)
|
||||
cppbessot_db_action_require_var(DB_MIGRATE_WITH)
|
||||
cppbessot_db_action_require_var(DB_TARGET)
|
||||
cppbessot_db_action_require_var(DB_SCHEMA_DIR_TO_GENERATE)
|
||||
cppbessot_db_action_require_var(DB_CREATEFROM_SCHEMA_DIR)
|
||||
|
||||
cppbessot_db_action_validate_db_target("${DB_TARGET}")
|
||||
cppbessot_db_action_assert_migration_dir_exists("${DB_MIGRATE_WITH}")
|
||||
cppbessot_db_action_resolve_backend_for_target(
|
||||
_backend
|
||||
_sqlite_path
|
||||
_pgsql_connstr
|
||||
"${DB_TARGET}")
|
||||
cppbessot_db_action_prepare_proddev(
|
||||
"${DB_TARGET}"
|
||||
"${_backend}"
|
||||
"${DB_MIGRATE_PRODDEV_USE_STALE}"
|
||||
"${_sqlite_path}"
|
||||
"${_pgsql_connstr}")
|
||||
cppbessot_db_action_backend_subdir(_backend_subdir "${_backend}")
|
||||
cppbessot_db_action_get_migration_dir_path(_migration_dir "${DB_MIGRATE_WITH}")
|
||||
set(_sql_dir "${_migration_dir}/${_backend_subdir}")
|
||||
cppbessot_db_action_collect_migration_sql_files(_sql_files "${_sql_dir}")
|
||||
cppbessot_db_action_get_hook_path(_pre_hook "${_migration_dir}" "pre-structural-backfill.sh")
|
||||
cppbessot_db_action_get_hook_path(_post_hook "${_migration_dir}" "post-structural-backfill.sh")
|
||||
|
||||
cppbessot_db_action_run_hook(
|
||||
"${_pre_hook}"
|
||||
"${DB_TARGET}"
|
||||
"${_backend}"
|
||||
"${_migration_dir}"
|
||||
"${DB_MIGRATE_WITH}"
|
||||
"${DB_SCHEMA_DIR_TO_GENERATE}"
|
||||
"${DB_CREATEFROM_SCHEMA_DIR}"
|
||||
"${_sqlite_path}"
|
||||
"${_pgsql_connstr}")
|
||||
|
||||
if(_sql_files)
|
||||
if("${_backend}" STREQUAL "sqlite")
|
||||
cppbessot_db_action_apply_sqlite_files("${_sqlite_path}" ${_sql_files})
|
||||
else()
|
||||
cppbessot_db_action_apply_pgsql_files("${_pgsql_connstr}" ${_sql_files})
|
||||
endif()
|
||||
endif()
|
||||
|
||||
cppbessot_db_action_run_hook(
|
||||
"${_post_hook}"
|
||||
"${DB_TARGET}"
|
||||
"${_backend}"
|
||||
"${_migration_dir}"
|
||||
"${DB_MIGRATE_WITH}"
|
||||
"${DB_SCHEMA_DIR_TO_GENERATE}"
|
||||
"${DB_CREATEFROM_SCHEMA_DIR}"
|
||||
"${_sqlite_path}"
|
||||
"${_pgsql_connstr}")
|
||||
@@ -1,4 +1,5 @@
|
||||
cmake_minimum_required(VERSION 3.16)
|
||||
include("${CMAKE_CURRENT_LIST_DIR}/cppbessotOdbHeaderDiscovery.cmake")
|
||||
|
||||
if(NOT DEFINED CPPBESSOT_ODB_EXECUTABLE OR CPPBESSOT_ODB_EXECUTABLE STREQUAL "")
|
||||
message(FATAL_ERROR "CPPBESSOT_ODB_EXECUTABLE is required")
|
||||
@@ -8,10 +9,7 @@ if(NOT DEFINED CPPBESSOT_VERSION_DIR OR CPPBESSOT_VERSION_DIR STREQUAL "")
|
||||
endif()
|
||||
|
||||
set(_include_dir "${CPPBESSOT_VERSION_DIR}/generated-cpp-source/include")
|
||||
file(GLOB _headers "${_include_dir}/*/model/*.h")
|
||||
if(NOT _headers)
|
||||
message(FATAL_ERROR "No model headers found under ${_include_dir}")
|
||||
endif()
|
||||
cppbessot_odb_find_object_headers(_object_headers "${_include_dir}")
|
||||
|
||||
foreach(_backend IN ITEMS sqlite pgsql)
|
||||
if(_backend STREQUAL "sqlite")
|
||||
@@ -25,7 +23,7 @@ foreach(_backend IN ITEMS sqlite pgsql)
|
||||
|
||||
execute_process(
|
||||
COMMAND "${CPPBESSOT_ODB_EXECUTABLE}" -I "${_include_dir}" --std c++11 -d "${_backend}" -q
|
||||
-o "${_out_dir}" --changelog-dir "${_out_dir}" ${_headers}
|
||||
-o "${_out_dir}" --changelog-dir "${_out_dir}" ${_object_headers}
|
||||
RESULT_VARIABLE _result
|
||||
OUTPUT_VARIABLE _stdout
|
||||
ERROR_VARIABLE _stderr
|
||||
|
||||
@@ -1,4 +1,5 @@
|
||||
cmake_minimum_required(VERSION 3.16)
|
||||
include("${CMAKE_CURRENT_LIST_DIR}/cppbessotOdbHeaderDiscovery.cmake")
|
||||
|
||||
if(NOT DEFINED CPPBESSOT_ODB_EXECUTABLE OR CPPBESSOT_ODB_EXECUTABLE STREQUAL "")
|
||||
message(FATAL_ERROR "CPPBESSOT_ODB_EXECUTABLE is required")
|
||||
@@ -13,36 +14,115 @@ if(NOT DEFINED CPPBESSOT_MIGRATION_DIR OR CPPBESSOT_MIGRATION_DIR STREQUAL "")
|
||||
message(FATAL_ERROR "CPPBESSOT_MIGRATION_DIR is required")
|
||||
endif()
|
||||
|
||||
set(_to_include_dir "${CPPBESSOT_TO_VERSION_DIR}/generated-cpp-source/include")
|
||||
file(GLOB _to_headers "${_to_include_dir}/*/model/*.h")
|
||||
if(NOT _to_headers)
|
||||
message(FATAL_ERROR "No target-version headers found under ${_to_include_dir}")
|
||||
endif()
|
||||
|
||||
foreach(_backend IN ITEMS sqlite pgsql)
|
||||
if(_backend STREQUAL "sqlite")
|
||||
set(_subdir sqlite)
|
||||
function(cppbessot_migration_resolve_backends out_var)
|
||||
if(NOT DEFINED CPPBESSOT_GEN_MIGRATION_BACKENDS OR CPPBESSOT_GEN_MIGRATION_BACKENDS STREQUAL "")
|
||||
if(DEFINED CPPBESSOT_GEN_MIGRATIONS_BACKENDS AND NOT CPPBESSOT_GEN_MIGRATIONS_BACKENDS STREQUAL "")
|
||||
set(_raw_backends "${CPPBESSOT_GEN_MIGRATIONS_BACKENDS}")
|
||||
else()
|
||||
set(_subdir postgre)
|
||||
set(_raw_backends "sqlite;pgsql")
|
||||
endif()
|
||||
else()
|
||||
set(_raw_backends "${CPPBESSOT_GEN_MIGRATION_BACKENDS}")
|
||||
endif()
|
||||
|
||||
set(_resolved_backends "")
|
||||
foreach(_backend IN LISTS _raw_backends)
|
||||
string(STRIP "${_backend}" _backend)
|
||||
string(TOLOWER "${_backend}" _backend)
|
||||
|
||||
if(_backend STREQUAL "")
|
||||
message(FATAL_ERROR "CPPBESSOT_GEN_MIGRATION_BACKENDS contains an empty backend name.")
|
||||
endif()
|
||||
|
||||
if(NOT _backend STREQUAL "sqlite" AND NOT _backend STREQUAL "pgsql")
|
||||
message(FATAL_ERROR
|
||||
"Unsupported migration backend `${_backend}`. "
|
||||
"CPPBESSOT_GEN_MIGRATION_BACKENDS supports sqlite and pgsql.")
|
||||
endif()
|
||||
|
||||
if(NOT _backend IN_LIST _resolved_backends)
|
||||
list(APPEND _resolved_backends "${_backend}")
|
||||
endif()
|
||||
endforeach()
|
||||
|
||||
if(NOT _resolved_backends)
|
||||
message(FATAL_ERROR "CPPBESSOT_GEN_MIGRATION_BACKENDS must name at least one backend.")
|
||||
endif()
|
||||
|
||||
set(${out_var} "${_resolved_backends}" PARENT_SCOPE)
|
||||
endfunction()
|
||||
|
||||
function(cppbessot_migration_backend_metadata backend out_subdir out_odb_database out_database_attr)
|
||||
if("${backend}" STREQUAL "sqlite")
|
||||
set(${out_subdir} "sqlite" PARENT_SCOPE)
|
||||
set(${out_odb_database} "sqlite" PARENT_SCOPE)
|
||||
set(${out_database_attr} "sqlite" PARENT_SCOPE)
|
||||
elseif("${backend}" STREQUAL "pgsql")
|
||||
set(${out_subdir} "postgre" PARENT_SCOPE)
|
||||
set(${out_odb_database} "pgsql" PARENT_SCOPE)
|
||||
set(${out_database_attr} "pgsql" PARENT_SCOPE)
|
||||
else()
|
||||
message(FATAL_ERROR "Unsupported migration backend `${backend}`.")
|
||||
endif()
|
||||
endfunction()
|
||||
|
||||
function(cppbessot_migration_write_empty_changelog changelog_path database_attr)
|
||||
if(NOT EXISTS "${changelog_path}")
|
||||
file(WRITE "${changelog_path}"
|
||||
"<changelog xmlns=\"http://www.codesynthesis.com/xmlns/odb/changelog\" database=\"${database_attr}\" version=\"1\">\n"
|
||||
" <model version=\"1\">\n"
|
||||
" </model>\n"
|
||||
"</changelog>\n")
|
||||
endif()
|
||||
endfunction()
|
||||
|
||||
function(cppbessot_migration_should_retry_sqlite_from_empty out_var backend from_xml empty_changelog stdout stderr)
|
||||
set(_should_retry FALSE)
|
||||
if("${backend}" STREQUAL "sqlite" AND NOT "${from_xml}" STREQUAL "${empty_changelog}")
|
||||
set(_diagnostics "${stdout}\n${stderr}")
|
||||
if(_diagnostics MATCHES "SQLite does not support dropping of columns")
|
||||
set(_should_retry TRUE)
|
||||
endif()
|
||||
endif()
|
||||
set(${out_var} "${_should_retry}" PARENT_SCOPE)
|
||||
endfunction()
|
||||
|
||||
cppbessot_migration_resolve_backends(_migration_backends)
|
||||
|
||||
set(_to_include_dir "${CPPBESSOT_TO_VERSION_DIR}/generated-cpp-source/include")
|
||||
cppbessot_odb_find_object_headers(_object_headers "${_to_include_dir}")
|
||||
|
||||
set(_empty_changelog_dir "${CPPBESSOT_MIGRATION_DIR}/.empty-baseline")
|
||||
file(MAKE_DIRECTORY "${_empty_changelog_dir}")
|
||||
|
||||
foreach(_backend IN LISTS _migration_backends)
|
||||
cppbessot_migration_backend_metadata(
|
||||
"${_backend}"
|
||||
_subdir
|
||||
_odb_database
|
||||
_database_attr)
|
||||
|
||||
set(_empty_changelog "${_empty_changelog_dir}/${_subdir}-empty.xml")
|
||||
cppbessot_migration_write_empty_changelog("${_empty_changelog}" "${_database_attr}")
|
||||
|
||||
set(_migration_backend_dir "${CPPBESSOT_MIGRATION_DIR}/${_subdir}")
|
||||
file(MAKE_DIRECTORY "${_migration_backend_dir}")
|
||||
|
||||
foreach(_header IN LISTS _to_headers)
|
||||
foreach(_header IN LISTS _object_headers)
|
||||
get_filename_component(_name "${_header}" NAME_WE)
|
||||
set(_in_xml "${CPPBESSOT_FROM_VERSION_DIR}/generated-odb-source/${_subdir}/${_name}.xml")
|
||||
set(_out_xml "${CPPBESSOT_TO_VERSION_DIR}/generated-odb-source/${_subdir}/${_name}.xml")
|
||||
|
||||
if(NOT EXISTS "${_in_xml}")
|
||||
message(FATAL_ERROR "Missing changelog input for `${_name}`: ${_in_xml}")
|
||||
set(_from_xml "${_in_xml}")
|
||||
if(NOT EXISTS "${_from_xml}")
|
||||
set(_from_xml "${_empty_changelog}")
|
||||
endif()
|
||||
|
||||
execute_process(
|
||||
COMMAND "${CPPBESSOT_ODB_EXECUTABLE}" -I "${_to_include_dir}" --std c++11 -d "${_backend}"
|
||||
COMMAND "${CPPBESSOT_ODB_EXECUTABLE}" -I "${_to_include_dir}" --std c++11 -d "${_odb_database}"
|
||||
--generate-schema --schema-format sql -q
|
||||
-o "${_migration_backend_dir}"
|
||||
--changelog-in "${_in_xml}"
|
||||
--changelog-in "${_from_xml}"
|
||||
--changelog-out "${_out_xml}"
|
||||
"${_header}"
|
||||
RESULT_VARIABLE _result
|
||||
@@ -50,6 +130,31 @@ foreach(_backend IN ITEMS sqlite pgsql)
|
||||
ERROR_VARIABLE _stderr
|
||||
)
|
||||
|
||||
cppbessot_migration_should_retry_sqlite_from_empty(
|
||||
_retry_from_empty
|
||||
"${_backend}"
|
||||
"${_from_xml}"
|
||||
"${_empty_changelog}"
|
||||
"${_stdout}"
|
||||
"${_stderr}")
|
||||
|
||||
if(NOT _result EQUAL 0 AND _retry_from_empty)
|
||||
message(WARNING
|
||||
"SQLite incremental migration failed for `${_name}`; retrying from empty baseline. "
|
||||
"Generated SQLite migration SQL is a greenfield create for this model.")
|
||||
execute_process(
|
||||
COMMAND "${CPPBESSOT_ODB_EXECUTABLE}" -I "${_to_include_dir}" --std c++11 -d "${_odb_database}"
|
||||
--generate-schema --schema-format sql -q
|
||||
-o "${_migration_backend_dir}"
|
||||
--changelog-in "${_empty_changelog}"
|
||||
--changelog-out "${_out_xml}"
|
||||
"${_header}"
|
||||
RESULT_VARIABLE _result
|
||||
OUTPUT_VARIABLE _stdout
|
||||
ERROR_VARIABLE _stderr
|
||||
)
|
||||
endif()
|
||||
|
||||
if(NOT _result EQUAL 0)
|
||||
message(FATAL_ERROR
|
||||
"Migration generation failed for `${_name}` backend `${_backend}`.\n${_stdout}\n${_stderr}")
|
||||
|
||||
@@ -1,4 +1,5 @@
|
||||
cmake_minimum_required(VERSION 3.16)
|
||||
include("${CMAKE_CURRENT_LIST_DIR}/cppbessotOdbHeaderDiscovery.cmake")
|
||||
|
||||
if(NOT DEFINED CPPBESSOT_ODB_EXECUTABLE OR CPPBESSOT_ODB_EXECUTABLE STREQUAL "")
|
||||
message(FATAL_ERROR "CPPBESSOT_ODB_EXECUTABLE is required")
|
||||
@@ -8,10 +9,7 @@ if(NOT DEFINED CPPBESSOT_VERSION_DIR OR CPPBESSOT_VERSION_DIR STREQUAL "")
|
||||
endif()
|
||||
|
||||
set(_include_dir "${CPPBESSOT_VERSION_DIR}/generated-cpp-source/include")
|
||||
file(GLOB _headers "${_include_dir}/*/model/*.h")
|
||||
if(NOT _headers)
|
||||
message(FATAL_ERROR "No model headers found under ${_include_dir}")
|
||||
endif()
|
||||
cppbessot_odb_find_object_headers(_object_headers "${_include_dir}")
|
||||
|
||||
foreach(_backend IN ITEMS sqlite pgsql)
|
||||
if(_backend STREQUAL "sqlite")
|
||||
@@ -28,7 +26,7 @@ foreach(_backend IN ITEMS sqlite pgsql)
|
||||
execute_process(
|
||||
COMMAND "${CPPBESSOT_ODB_EXECUTABLE}" -I "${_include_dir}" --std c++11 -d "${_backend}"
|
||||
--generate-schema --schema-format sql -q
|
||||
-o "${_ddl_dir}" --changelog-dir "${_changelog_dir}" ${_headers}
|
||||
-o "${_ddl_dir}" --changelog-dir "${_changelog_dir}" ${_object_headers}
|
||||
RESULT_VARIABLE _result
|
||||
OUTPUT_VARIABLE _stdout
|
||||
ERROR_VARIABLE _stderr
|
||||
|
||||
@@ -0,0 +1,23 @@
|
||||
Ok. Now we need to add the concept of migration action targets. Add new vars: these control the global migration action target. These don't determine the generated source oas, or the target dir to generate that oas into. Rather, they determine the target DB to apply the current DB_SCHEMA_DIR_TO_GENERATE schema against.
|
||||
|
||||
The new var name is: DB_TARGET=<prod|proddev|dev>.
|
||||
It's expected by the parent source dir that DB_TARGET either already is already migrated to DB_SCHEMA_DIR_TO_GENERATE; or DB_TARGET will be migrated to DB_SCHEMA_DIR_TO_GENERATE before the application is compiled and executed. It is in fact the parent application that sets DB_SCHEMA_DIR_TO_GENERATE, after all.
|
||||
|
||||
Prod and dev are self-sufficient targets. Migration actions applied to them are applied to them qua independent, persistent db.
|
||||
* Action: target db_createfrom. Accepts option DB_CREATEFROM_SCHEMA_DIR=<schema dir>. Respects and uses CPPBESSOT_WORKDIR. Will use the pre-generated sql_ddl artifacts in <schema dir> to idempotently create a new db with the name prod or dev, depending on the value of DB_TARGET. Default ON_TARGET=dev. Default value of DB_CREATEFROM_SCHEMA_DIR is DB_SCHEMA_DIR_TO_GENERATE. Obv a non-existent DB_CREATEFROM_SCHEMA_DIR or one whose oas spec doesn't exist, will result in an abort.
|
||||
|
||||
Proddev is copied from prod. It is a sandbox target used to apply migrations against a clone of prod.
|
||||
* Proddev does not support the db_createfrom target. It is always cloned from prod.
|
||||
* Action: target db_migrate with DB_TARGET=proddev will always automatically and idempotently clone prod to a copy called "proddev", unless the DB_MIGRATE_PRODDEV_USE_STALE=ON flag is set. This flag simply means "Don't clone. Re-run the current target action against the most recent, stale cloned proddev, which is itself a prior clone of prod". Specifying DB_TARGET=proddev and running db_createfrom is illegal and should abort. Speciying DB_MIGRATE_PRODDEV_USE_STALE=ON and running target db_migrate when no current stale prod DB exists results in an abort.
|
||||
|
||||
Action: Target db_migrate: Option DB_TARGET=<prod|proddev|dev>. Option DB_MIGRATE_WITH=<migration from-to-dir>. If the <migration from-to-dir> doesn't exist, this aborts.
|
||||
|
||||
Each DB migration from-to-dir may have two shell scripts: pre-structural-backfill.sh and post-structural-backfill.sh. The db_migrate target should check to see if these scripts exist, and if the pre-structural-backfill.sh script exists, it should be executed before the structural migrations, if any. If the structural migrations generated by the db_gen_migrations have content, then they should be applied next. Then if the post-structural-backfill.sh script exists, it should be run last.
|
||||
|
||||
Think of as many tests as you can to give coverage of all of these new commands as well as the sqlite and pgsql variants' behaviour and write them first before implementing this plan.
|
||||
|
||||
Be very aggressive about splitting and isolating code into common subfunctions and modules, and reusing wherever possible. Don't duplicate code. No long-scrolling functions.
|
||||
|
||||
----
|
||||
That sqlite_apply.py script: can it be done as a bash script instead?
|
||||
I'd prefer to add the sqlite3 cli to the project's CMake find_* targets, than to add python unnecessarily.
|
||||
@@ -0,0 +1,17 @@
|
||||
Ok. We've added a new db, test, and we should now add a new target for DB_TARGET. Add
|
||||
the new DB_TARGET, "tests", so that from now on, migration actions like db_createfrom
|
||||
and db_migrate can choose to target DB_TARGET=<dev|prod|proddev|tests>. Ofc, proddev
|
||||
cannot have a db_createfrom command.
|
||||
|
||||
Also the tests that need DB access should now use the CPPBESSOT_DB_[PGSQL|SQLITE]_PRODDEV_CONNSTR. So you should get rid of the legacy ODB test connstr variables and replace them with CPPBESSOT_DB_[PGSQL|SQLITE]_PRODDEV_CONNSTR. This unifies everything nicely. So now we have 4 DB targets, both for Postgre and sqlite.
|
||||
|
||||
The legacy db-action PostgreSQL admin connstr variable should be removed as well.
|
||||
|
||||
The local YugabyteDB instance is configured at /media/latentprion/aafe96c9-7fcd-40ce-991d-ca2d23b5ba17/db/yugabytedb. The runtime proddev mapping should remain the real proddev DB (`couresilient_proddev`) with the real app user (`couresilient`). The separate `couresilient_tests` user and `couresilient_tests` DB are for the `DB_TARGET=tests` mapping, not for `DB_TARGET=proddev`.
|
||||
|
||||
If there are tests whose purpose is to test the clone command from prod to proddev, have them use the same clone hook as production: CPPBESSOT_DB_[PGSQL|SQLITE]_CLONE_PROD_TO_PRODDEV_COMMAND.
|
||||
|
||||
No backward compatibility or anything. Hard cutover.
|
||||
----
|
||||
|
||||
Make sure to update cppbessot.env.cmake[.example] and README.md inside of couresilient.
|
||||
@@ -0,0 +1,12 @@
|
||||
Ok. Let's keep the cppbessot_add_generated_libraries target, but make it actually depend on two new targets:
|
||||
cppbessot_add_generated_cpp_model_libraries and cppbessot_add_generated_odb_libraries.
|
||||
|
||||
cppbessot_add_generated_odb_libraries adds the two cppbessot::odb_* lib targets.
|
||||
|
||||
The cppbessot_add_generated_cpp_model_libraries target should depend on the db_gen_cpp_headers target, and so a program that links against cppbessot::openai_model_gen should build the db_gen_cpp_headers target for the current DB_SCHEMA_DIR_TO_GENERATE first, during the build stage.
|
||||
|
||||
Similarly the two cppbessot::odb_* library targets should depend on the db_gen_odb_logic target, and a program that depends on that cppbessot::odb_* targets should automatically build db_gen_odb_logic target for the current DB_SCHEMA_DIR_TO_GENERATE first, at build time.
|
||||
|
||||
Eradicate SCHEMA_DIR completely, and leave behind only DB_SCHEMA_DIR_TO_GENERATE.
|
||||
|
||||
cmake configure step should fail if DB_SCHEMA_DIR_TO_GENERATE doesn't exist, or if the openapi model which it uses as its SSOT doesn't exist.
|
||||
@@ -13,6 +13,12 @@
|
||||
#include <nlohmann/json.hpp>
|
||||
#include <odb/core.hxx>
|
||||
|
||||
{{#vars}}
|
||||
{{#vendorExtensions.x-cppType}}
|
||||
#include <cppbessot/model/{{vendorExtensions.x-cppType}}.h>
|
||||
{{/vendorExtensions.x-cppType}}
|
||||
{{/vars}}
|
||||
|
||||
namespace models {
|
||||
|
||||
{{#isEnum}}
|
||||
@@ -41,8 +47,11 @@ public:
|
||||
{{#vendorExtensions.x-odbAddedIn}}
|
||||
// odbAddedIn: {{.}}
|
||||
{{/vendorExtensions.x-odbAddedIn}}
|
||||
{{#isString}}std::string{{/isString}}{{^isString}}{{#isDateTime}}std::string{{/isDateTime}}{{^isDateTime}}{{dataType}}{{/isDateTime}}{{/isString}} {{nameInCamelCase}}{};
|
||||
{{#vendorExtensions.x-cppType}}{{vendorExtensions.x-cppType}}{{/vendorExtensions.x-cppType}}{{^vendorExtensions.x-cppType}}{{#isString}}std::string{{/isString}}{{^isString}}{{#isDateTime}}std::string{{/isDateTime}}{{^isDateTime}}{{#isBoolean}}bool{{/isBoolean}}{{^isBoolean}}{{#isInteger}}int32_t{{/isInteger}}{{^isInteger}}{{#isLong}}int64_t{{/isLong}}{{^isLong}}{{dataType}}{{/isLong}}{{/isInteger}}{{/isBoolean}}{{/isDateTime}}{{/isString}}{{/vendorExtensions.x-cppType}} {{nameInCamelCase}}{};
|
||||
{{/vars}}
|
||||
{{#vendorExtensions.x-dataModelUniqueConstraints}}
|
||||
#pragma db index("{{name}}") unique {{odbMemberSpec}}
|
||||
{{/vendorExtensions.x-dataModelUniqueConstraints}}
|
||||
|
||||
NLOHMANN_DEFINE_TYPE_INTRUSIVE({{classname}}{{#vars}}, {{nameInCamelCase}}{{/vars}})
|
||||
|
||||
|
||||
Generated
+2776
File diff suppressed because it is too large
Load Diff
@@ -0,0 +1,8 @@
|
||||
{
|
||||
"name": "cppbessot",
|
||||
"private": true,
|
||||
"devDependencies": {
|
||||
"@openapitools/openapi-generator-cli": "^2.30.0",
|
||||
"openapi-zod-client": "^1.18.3"
|
||||
}
|
||||
}
|
||||
@@ -6,3 +6,4 @@ endif()
|
||||
add_subdirectory(googletest)
|
||||
add_subdirectory(cpp-serdes)
|
||||
add_subdirectory(odb-orm)
|
||||
add_subdirectory(db-actions)
|
||||
|
||||
@@ -0,0 +1,93 @@
|
||||
function(_cppbessot_db_action_test_common_args out_var test_name script_name)
|
||||
set(_args
|
||||
"-DCPPBESSOT_TEST_NAME=${test_name}"
|
||||
"-DCPPBESSOT_TEST_BINARY_DIR=${CMAKE_CURRENT_BINARY_DIR}"
|
||||
"-DCPPBESSOT_TEST_MODULE_SOURCE_DIR=${PROJECT_SOURCE_DIR}"
|
||||
"-DCPPBESSOT_DB_PGSQL_DEV_CONNSTR=${CPPBESSOT_DB_PGSQL_DEV_CONNSTR}"
|
||||
"-DCPPBESSOT_DB_PGSQL_PROD_CONNSTR=${CPPBESSOT_DB_PGSQL_PROD_CONNSTR}"
|
||||
"-DCPPBESSOT_DB_PGSQL_PRODDEV_CONNSTR=${CPPBESSOT_DB_PGSQL_PRODDEV_CONNSTR}"
|
||||
"-DCPPBESSOT_DB_PGSQL_TESTS_CONNSTR=${CPPBESSOT_DB_PGSQL_TESTS_CONNSTR}"
|
||||
"-DCPPBESSOT_DB_PGSQL_CLONE_PROD_TO_PRODDEV_COMMAND=${CPPBESSOT_DB_PGSQL_CLONE_PROD_TO_PRODDEV_COMMAND}"
|
||||
"-DCPPBESSOT_DB_SQLITE_CLONE_PROD_TO_PRODDEV_COMMAND=${CPPBESSOT_DB_SQLITE_CLONE_PROD_TO_PRODDEV_COMMAND}"
|
||||
"-DCPPBESSOT_DB_ACTION_TEST_SCRIPT=${CMAKE_CURRENT_SOURCE_DIR}/scripts/${script_name}")
|
||||
set(${out_var} "${_args}" PARENT_SCOPE)
|
||||
endfunction()
|
||||
|
||||
function(cppbessot_add_db_action_test test_name script_name)
|
||||
_cppbessot_db_action_test_common_args(_test_args "${test_name}" "${script_name}")
|
||||
add_test(
|
||||
NAME ${test_name}
|
||||
COMMAND "${CMAKE_COMMAND}" ${_test_args} -P "${CMAKE_CURRENT_SOURCE_DIR}/scripts/${script_name}")
|
||||
endfunction()
|
||||
|
||||
function(cppbessot_add_real_pgsql_db_action_test test_name script_name)
|
||||
find_program(_psql psql)
|
||||
if(NOT _psql)
|
||||
message(STATUS "Skipping real PostgreSQL db-action test `${test_name}` because `psql` is not available.")
|
||||
return()
|
||||
endif()
|
||||
find_program(_pg_dump pg_dump)
|
||||
if(NOT _pg_dump)
|
||||
message(STATUS "Skipping real PostgreSQL db-action test `${test_name}` because `pg_dump` is not available.")
|
||||
return()
|
||||
endif()
|
||||
|
||||
if("${CPPBESSOT_DB_PGSQL_PRODDEV_CONNSTR}" STREQUAL "")
|
||||
message(STATUS "Skipping real PostgreSQL db-action test `${test_name}` because CPPBESSOT_DB_PGSQL_PRODDEV_CONNSTR is empty.")
|
||||
return()
|
||||
endif()
|
||||
|
||||
foreach(_required_var IN LISTS ARGN)
|
||||
if("${${_required_var}}" STREQUAL "")
|
||||
message(STATUS "Skipping real PostgreSQL db-action test `${test_name}` because ${_required_var} is empty.")
|
||||
return()
|
||||
endif()
|
||||
endforeach()
|
||||
|
||||
_cppbessot_db_action_test_common_args(_test_args "${test_name}" "${script_name}")
|
||||
add_test(
|
||||
NAME ${test_name}
|
||||
COMMAND "${CMAKE_COMMAND}" ${_test_args} -P "${CMAKE_CURRENT_SOURCE_DIR}/scripts/${script_name}")
|
||||
set_tests_properties(${test_name} PROPERTIES RUN_SERIAL TRUE)
|
||||
endfunction()
|
||||
|
||||
cppbessot_add_db_action_test(cppbessot_db_action_invalid_target invalid_target.cmake)
|
||||
cppbessot_add_db_action_test(cppbessot_db_action_missing_mapping missing_mapping.cmake)
|
||||
cppbessot_add_db_action_test(cppbessot_db_action_ambiguous_mapping ambiguous_mapping.cmake)
|
||||
cppbessot_add_db_action_test(cppbessot_db_action_missing_migrate_with missing_migrate_with.cmake)
|
||||
cppbessot_add_db_action_test(cppbessot_db_action_missing_migration_dir missing_migration_dir.cmake)
|
||||
cppbessot_add_db_action_test(cppbessot_db_action_proddev_createfrom_illegal proddev_createfrom_illegal.cmake)
|
||||
cppbessot_add_db_action_test(cppbessot_db_action_sqlite_createfrom sqlite_createfrom.cmake)
|
||||
cppbessot_add_db_action_test(cppbessot_db_action_sqlite_tests_createfrom sqlite_tests_createfrom.cmake)
|
||||
cppbessot_add_db_action_test(cppbessot_db_action_sqlite_invalid_sql sqlite_invalid_sql.cmake)
|
||||
cppbessot_add_db_action_test(cppbessot_db_action_sqlite_migrate sqlite_migrate.cmake)
|
||||
cppbessot_add_db_action_test(cppbessot_db_action_sqlite_proddev_stale sqlite_proddev_stale.cmake)
|
||||
cppbessot_add_db_action_test(cppbessot_db_action_sqlite_proddev_clone sqlite_proddev_clone.cmake)
|
||||
cppbessot_add_db_action_test(cppbessot_db_action_pgsql_createfrom_mock pgsql_createfrom_mock.cmake)
|
||||
cppbessot_add_db_action_test(cppbessot_db_action_pgsql_tests_createfrom_mock pgsql_tests_createfrom_mock.cmake)
|
||||
cppbessot_add_db_action_test(cppbessot_db_action_pgsql_migrate_order pgsql_migrate_order.cmake)
|
||||
cppbessot_add_db_action_test(cppbessot_db_action_pgsql_stale_abort pgsql_stale_abort.cmake)
|
||||
cppbessot_add_db_action_test(cppbessot_db_action_backfill_env_no_structural backfill_env_no_structural.cmake)
|
||||
cppbessot_add_db_action_test(cppbessot_db_gen_migrations_policy migration_policy.cmake)
|
||||
cppbessot_add_db_action_test(cppbessot_openapi_odb_table_filter openapi_odb_table_filter.cmake)
|
||||
cppbessot_add_real_pgsql_db_action_test(
|
||||
cppbessot_db_action_pgsql_createfrom_real
|
||||
pgsql_createfrom_real.cmake
|
||||
CPPBESSOT_DB_PGSQL_PRODDEV_CONNSTR)
|
||||
cppbessot_add_real_pgsql_db_action_test(
|
||||
cppbessot_db_action_pgsql_migrate_real
|
||||
pgsql_migrate_real.cmake
|
||||
CPPBESSOT_DB_PGSQL_PRODDEV_CONNSTR)
|
||||
cppbessot_add_real_pgsql_db_action_test(
|
||||
cppbessot_db_action_pgsql_proddev_clone_real
|
||||
pgsql_proddev_clone_real.cmake
|
||||
CPPBESSOT_DB_PGSQL_PRODDEV_CONNSTR)
|
||||
cppbessot_add_real_pgsql_db_action_test(
|
||||
cppbessot_db_action_pgsql_proddev_stale_real
|
||||
pgsql_proddev_stale_real.cmake
|
||||
CPPBESSOT_DB_PGSQL_PRODDEV_CONNSTR)
|
||||
cppbessot_add_real_pgsql_db_action_test(
|
||||
cppbessot_db_action_pgsql_backfill_real
|
||||
pgsql_backfill_real.cmake
|
||||
CPPBESSOT_DB_PGSQL_PRODDEV_CONNSTR)
|
||||
cppbessot_add_db_action_test(cppbessot_db_action_regression regression_targets.cmake)
|
||||
@@ -0,0 +1,447 @@
|
||||
cmake_minimum_required(VERSION 3.16)
|
||||
|
||||
function(cppbessot_test_require_var var_name)
|
||||
if(NOT DEFINED ${var_name} OR "${${var_name}}" STREQUAL "")
|
||||
message(FATAL_ERROR "Missing required test variable `${var_name}`.")
|
||||
endif()
|
||||
endfunction()
|
||||
|
||||
function(cppbessot_test_reset_dir path)
|
||||
file(REMOVE_RECURSE "${path}")
|
||||
file(MAKE_DIRECTORY "${path}")
|
||||
endfunction()
|
||||
|
||||
function(cppbessot_test_write_file path)
|
||||
get_filename_component(_parent "${path}" DIRECTORY)
|
||||
if(NOT "${_parent}" STREQUAL "")
|
||||
file(MAKE_DIRECTORY "${_parent}")
|
||||
endif()
|
||||
string(JOIN "" _content ${ARGN})
|
||||
file(WRITE "${path}" "${_content}")
|
||||
endfunction()
|
||||
|
||||
function(cppbessot_test_cache_string_setting out_var var_name value)
|
||||
set(${out_var} "set(${var_name} \"${value}\" CACHE STRING \"\")\n" PARENT_SCOPE)
|
||||
endfunction()
|
||||
|
||||
function(cppbessot_test_write_shell_script path)
|
||||
cppbessot_test_write_file("${path}" ${ARGN})
|
||||
execute_process(COMMAND chmod +x "${path}")
|
||||
endfunction()
|
||||
|
||||
function(cppbessot_test_case_dir out_var)
|
||||
cppbessot_test_require_var(CPPBESSOT_TEST_BINARY_DIR)
|
||||
cppbessot_test_require_var(CPPBESSOT_TEST_NAME)
|
||||
set(_case_dir "${CPPBESSOT_TEST_BINARY_DIR}/cases/${CPPBESSOT_TEST_NAME}")
|
||||
cppbessot_test_reset_dir("${_case_dir}")
|
||||
set(${out_var} "${_case_dir}" PARENT_SCOPE)
|
||||
endfunction()
|
||||
|
||||
function(cppbessot_test_write_project root_dir settings_text)
|
||||
cppbessot_test_require_var(CPPBESSOT_TEST_MODULE_SOURCE_DIR)
|
||||
string(REPLACE "\\" "\\\\" _module_dir "${CPPBESSOT_TEST_MODULE_SOURCE_DIR}")
|
||||
set(_settings_parts "${settings_text}" ${ARGN})
|
||||
set(_post_include_text
|
||||
"cppbessot_add_db_createfrom_target()\n"
|
||||
"cppbessot_add_db_migrate_target()\n")
|
||||
list(LENGTH _settings_parts _settings_len)
|
||||
if(_settings_len GREATER 0)
|
||||
math(EXPR _last_index "${_settings_len} - 1")
|
||||
list(GET _settings_parts "${_last_index}" _last_part)
|
||||
if("${_last_part}" STREQUAL "FULL_ENABLE")
|
||||
list(REMOVE_AT _settings_parts "${_last_index}")
|
||||
set(_post_include_text "cppbessot_enable()\n")
|
||||
endif()
|
||||
endif()
|
||||
string(JOIN "" _settings_text ${_settings_parts})
|
||||
cppbessot_test_write_file(
|
||||
"${root_dir}/CMakeLists.txt"
|
||||
"cmake_minimum_required(VERSION 3.20)\n"
|
||||
"project(cppbessot_db_action_fixture LANGUAGES CXX)\n"
|
||||
"set(CPPBESSOT_WORKDIR \"db\" CACHE STRING \"\")\n"
|
||||
"set(DB_SCHEMA_DIR_TO_GENERATE \"v1.1\" CACHE STRING \"\")\n"
|
||||
"set(CPPBESSOT_AUTO_ENABLE OFF CACHE BOOL \"\")\n"
|
||||
"${_settings_text}\n"
|
||||
"include(\"${_module_dir}/cmake/CppBeSSOT.cmake\")\n"
|
||||
"${_post_include_text}")
|
||||
endfunction()
|
||||
|
||||
function(cppbessot_test_add_schema root_dir schema_dir)
|
||||
cppbessot_test_write_file(
|
||||
"${root_dir}/db/${schema_dir}/openapi/openapi.yaml"
|
||||
"openapi: 3.0.0\n"
|
||||
"info:\n"
|
||||
" title: test-${schema_dir}\n"
|
||||
" version: 1.0.0\n"
|
||||
"paths: {}\n"
|
||||
"components:\n"
|
||||
" schemas:\n"
|
||||
" Agent:\n"
|
||||
" type: object\n"
|
||||
" properties:\n"
|
||||
" id:\n"
|
||||
" type: string\n")
|
||||
endfunction()
|
||||
|
||||
function(cppbessot_test_add_sql_file path content)
|
||||
string(JOIN ";" _sql_text "${content}" ${ARGN})
|
||||
get_filename_component(_parent "${path}" DIRECTORY)
|
||||
if(NOT "${_parent}" STREQUAL "")
|
||||
file(MAKE_DIRECTORY "${_parent}")
|
||||
endif()
|
||||
file(WRITE "${path}" "${_sql_text}")
|
||||
endfunction()
|
||||
|
||||
function(cppbessot_test_configure_project root_dir build_dir result_var stdout_var stderr_var)
|
||||
execute_process(
|
||||
COMMAND "${CMAKE_COMMAND}" -S "${root_dir}" -B "${build_dir}"
|
||||
RESULT_VARIABLE _result
|
||||
OUTPUT_VARIABLE _stdout
|
||||
ERROR_VARIABLE _stderr
|
||||
)
|
||||
set(${result_var} "${_result}" PARENT_SCOPE)
|
||||
set(${stdout_var} "${_stdout}" PARENT_SCOPE)
|
||||
set(${stderr_var} "${_stderr}" PARENT_SCOPE)
|
||||
endfunction()
|
||||
|
||||
function(cppbessot_test_build_target build_dir target_name result_var stdout_var stderr_var)
|
||||
execute_process(
|
||||
COMMAND "${CMAKE_COMMAND}" --build "${build_dir}" --target "${target_name}"
|
||||
RESULT_VARIABLE _result
|
||||
OUTPUT_VARIABLE _stdout
|
||||
ERROR_VARIABLE _stderr
|
||||
)
|
||||
set(${result_var} "${_result}" PARENT_SCOPE)
|
||||
set(${stdout_var} "${_stdout}" PARENT_SCOPE)
|
||||
set(${stderr_var} "${_stderr}" PARENT_SCOPE)
|
||||
endfunction()
|
||||
|
||||
function(cppbessot_test_build_target_dry_run build_dir target_name result_var stdout_var stderr_var)
|
||||
execute_process(
|
||||
COMMAND "${CMAKE_COMMAND}" --build "${build_dir}" --target "${target_name}" -- -n
|
||||
RESULT_VARIABLE _result
|
||||
OUTPUT_VARIABLE _stdout
|
||||
ERROR_VARIABLE _stderr
|
||||
)
|
||||
set(${result_var} "${_result}" PARENT_SCOPE)
|
||||
set(${stdout_var} "${_stdout}" PARENT_SCOPE)
|
||||
set(${stderr_var} "${_stderr}" PARENT_SCOPE)
|
||||
endfunction()
|
||||
|
||||
function(cppbessot_test_assert_success result stderr context)
|
||||
if(NOT "${result}" STREQUAL "0")
|
||||
message(FATAL_ERROR "${context} unexpectedly failed.\n${stderr}")
|
||||
endif()
|
||||
endfunction()
|
||||
|
||||
function(cppbessot_test_assert_failure_contains result stderr needle)
|
||||
if("${result}" STREQUAL "0")
|
||||
message(FATAL_ERROR "Expected failure containing `${needle}`, but command succeeded.")
|
||||
endif()
|
||||
string(FIND "${stderr}" "${needle}" _match_index)
|
||||
if(_match_index EQUAL -1)
|
||||
message(FATAL_ERROR "Expected failure containing `${needle}`.\nActual stderr:\n${stderr}")
|
||||
endif()
|
||||
endfunction()
|
||||
|
||||
function(cppbessot_test_assert_contains haystack needle context)
|
||||
string(FIND "${haystack}" "${needle}" _match_index)
|
||||
if(_match_index EQUAL -1)
|
||||
message(FATAL_ERROR "${context}: expected to find `${needle}` in:\n${haystack}")
|
||||
endif()
|
||||
endfunction()
|
||||
|
||||
function(cppbessot_test_assert_file_exists path)
|
||||
if(NOT EXISTS "${path}")
|
||||
message(FATAL_ERROR "Expected file to exist: ${path}")
|
||||
endif()
|
||||
endfunction()
|
||||
|
||||
function(cppbessot_test_set_path_with_tool_dir tool_dir)
|
||||
if(DEFINED ENV{PATH} AND NOT "$ENV{PATH}" STREQUAL "")
|
||||
set(ENV{PATH} "${tool_dir}:$ENV{PATH}")
|
||||
else()
|
||||
set(ENV{PATH} "${tool_dir}")
|
||||
endif()
|
||||
endfunction()
|
||||
|
||||
function(cppbessot_test_has_real_pgsql_support out_var)
|
||||
find_program(_psql psql)
|
||||
find_program(_pg_dump pg_dump)
|
||||
if(_psql AND _pg_dump AND DEFINED CPPBESSOT_DB_PGSQL_PRODDEV_CONNSTR
|
||||
AND NOT "${CPPBESSOT_DB_PGSQL_PRODDEV_CONNSTR}" STREQUAL "")
|
||||
set(${out_var} TRUE PARENT_SCOPE)
|
||||
else()
|
||||
set(${out_var} FALSE PARENT_SCOPE)
|
||||
endif()
|
||||
endfunction()
|
||||
|
||||
function(cppbessot_test_require_real_pgsql_support)
|
||||
cppbessot_test_has_real_pgsql_support(_has_support)
|
||||
if(NOT _has_support)
|
||||
message(FATAL_ERROR
|
||||
"Real PostgreSQL db-action test support requires `psql`, `pg_dump`, and CPPBESSOT_DB_PGSQL_PRODDEV_CONNSTR.")
|
||||
endif()
|
||||
endfunction()
|
||||
|
||||
function(cppbessot_test_pgsql_admin_connstr out_var)
|
||||
cppbessot_test_require_var(CPPBESSOT_DB_PGSQL_PRODDEV_CONNSTR)
|
||||
cppbessot_test_pgsql_connstr_replace_dbname(_admin_connstr "${CPPBESSOT_DB_PGSQL_PRODDEV_CONNSTR}" "postgres")
|
||||
set(${out_var} "${_admin_connstr}" PARENT_SCOPE)
|
||||
endfunction()
|
||||
|
||||
function(cppbessot_test_pgsql_exec connstr sql_text)
|
||||
find_program(_psql psql REQUIRED)
|
||||
execute_process(
|
||||
COMMAND "${_psql}" "${connstr}" -v ON_ERROR_STOP=1 -c "${sql_text}"
|
||||
RESULT_VARIABLE _result
|
||||
OUTPUT_VARIABLE _stdout
|
||||
ERROR_VARIABLE _stderr
|
||||
)
|
||||
if(NOT _result EQUAL 0)
|
||||
message(FATAL_ERROR "Failed PostgreSQL SQL execution.\n${_stdout}\n${_stderr}")
|
||||
endif()
|
||||
endfunction()
|
||||
|
||||
function(cppbessot_test_pgsql_exec_file connstr sql_file)
|
||||
find_program(_psql psql REQUIRED)
|
||||
execute_process(
|
||||
COMMAND "${_psql}" "${connstr}" -v ON_ERROR_STOP=1 -f "${sql_file}"
|
||||
RESULT_VARIABLE _result
|
||||
OUTPUT_VARIABLE _stdout
|
||||
ERROR_VARIABLE _stderr
|
||||
)
|
||||
if(NOT _result EQUAL 0)
|
||||
message(FATAL_ERROR "Failed PostgreSQL SQL file execution for `${sql_file}`.\n${_stdout}\n${_stderr}")
|
||||
endif()
|
||||
endfunction()
|
||||
|
||||
function(cppbessot_test_pgsql_query_scalar out_var connstr query)
|
||||
find_program(_psql psql REQUIRED)
|
||||
execute_process(
|
||||
COMMAND "${_psql}" "${connstr}" -v ON_ERROR_STOP=1 -t -A -c "${query}"
|
||||
RESULT_VARIABLE _result
|
||||
OUTPUT_VARIABLE _stdout
|
||||
ERROR_VARIABLE _stderr
|
||||
OUTPUT_STRIP_TRAILING_WHITESPACE
|
||||
)
|
||||
if(NOT _result EQUAL 0)
|
||||
message(FATAL_ERROR "Failed PostgreSQL query.\n${_stderr}")
|
||||
endif()
|
||||
string(STRIP "${_stdout}" _value)
|
||||
set(${out_var} "${_value}" PARENT_SCOPE)
|
||||
endfunction()
|
||||
|
||||
function(cppbessot_test_pgsql_connstr_dbname out_var connstr)
|
||||
string(REGEX MATCH "(^|[ \t])dbname=([^ \t]+)" _match " ${connstr}")
|
||||
if("${CMAKE_MATCH_2}" STREQUAL "")
|
||||
message(FATAL_ERROR "Expected PostgreSQL connstr to include dbname=... but got `${connstr}`.")
|
||||
endif()
|
||||
set(${out_var} "${CMAKE_MATCH_2}" PARENT_SCOPE)
|
||||
endfunction()
|
||||
|
||||
function(cppbessot_test_pgsql_connstr_field out_var connstr field_name)
|
||||
string(REGEX MATCH "(^|[ \t])${field_name}=([^ \t]+)" _match " ${connstr}")
|
||||
if("${CMAKE_MATCH_2}" STREQUAL "")
|
||||
message(FATAL_ERROR
|
||||
"Expected PostgreSQL connstr to include `${field_name}=...` but got `${connstr}`.")
|
||||
endif()
|
||||
set(${out_var} "${CMAKE_MATCH_2}" PARENT_SCOPE)
|
||||
endfunction()
|
||||
|
||||
function(cppbessot_test_pgsql_connstr_replace_dbname out_var connstr new_dbname)
|
||||
cppbessot_test_pgsql_connstr_dbname(_old_dbname "${connstr}")
|
||||
string(REGEX REPLACE "(^|[ \t])dbname=[^ \t]+" "\\1dbname=${new_dbname}" _updated " ${connstr}")
|
||||
string(STRIP "${_updated}" _updated)
|
||||
set(${out_var} "${_updated}" PARENT_SCOPE)
|
||||
endfunction()
|
||||
|
||||
function(cppbessot_test_pgsql_unique_dbname out_var role_suffix)
|
||||
cppbessot_test_require_var(CPPBESSOT_TEST_NAME)
|
||||
cppbessot_test_require_var(CPPBESSOT_TEST_BINARY_DIR)
|
||||
string(TOLOWER "${CPPBESSOT_TEST_NAME}" _base)
|
||||
string(REGEX REPLACE "[^a-z0-9]+" "_" _base "${_base}")
|
||||
string(REGEX REPLACE "^_+|_+$" "" _base "${_base}")
|
||||
if("${_base}" STREQUAL "")
|
||||
set(_base "db_action_test")
|
||||
endif()
|
||||
string(TOLOWER "${role_suffix}" _role)
|
||||
string(REGEX REPLACE "[^a-z0-9]+" "_" _role "${_role}")
|
||||
string(REGEX REPLACE "^_+|_+$" "" _role "${_role}")
|
||||
string(SHA256 _scope_hash "${CPPBESSOT_TEST_BINARY_DIR}")
|
||||
string(SUBSTRING "${_scope_hash}" 0 8 _scope_suffix)
|
||||
set(${out_var} "cppbessot_${_base}_${_role}_${_scope_suffix}" PARENT_SCOPE)
|
||||
endfunction()
|
||||
|
||||
function(cppbessot_test_pgsql_isolated_connstr out_var base_connstr role_suffix)
|
||||
cppbessot_test_pgsql_unique_dbname(_dbname "${role_suffix}")
|
||||
cppbessot_test_pgsql_connstr_replace_dbname(_updated "${base_connstr}" "${_dbname}")
|
||||
set(${out_var} "${_updated}" PARENT_SCOPE)
|
||||
endfunction()
|
||||
|
||||
function(cppbessot_test_pgsql_escape_identifier out_var identifier)
|
||||
string(REPLACE "\"" "\"\"" _escaped "${identifier}")
|
||||
set(${out_var} "\"${_escaped}\"" PARENT_SCOPE)
|
||||
endfunction()
|
||||
|
||||
function(cppbessot_test_pgsql_escape_literal out_var literal)
|
||||
string(REPLACE "'" "''" _escaped "${literal}")
|
||||
set(${out_var} "'${_escaped}'" PARENT_SCOPE)
|
||||
endfunction()
|
||||
|
||||
function(cppbessot_test_pgsql_drop_database connstr)
|
||||
cppbessot_test_require_real_pgsql_support()
|
||||
cppbessot_test_pgsql_admin_connstr(_admin_connstr)
|
||||
cppbessot_test_pgsql_connstr_dbname(_dbname "${connstr}")
|
||||
cppbessot_test_pgsql_escape_identifier(_db_ident "${_dbname}")
|
||||
cppbessot_test_pgsql_escape_literal(_db_lit "${_dbname}")
|
||||
cppbessot_test_pgsql_exec(
|
||||
"${_admin_connstr}"
|
||||
"SELECT pg_terminate_backend(pid) FROM pg_stat_activity WHERE datname = ${_db_lit} AND pid <> pg_backend_pid();")
|
||||
cppbessot_test_pgsql_exec(
|
||||
"${_admin_connstr}"
|
||||
"DROP DATABASE IF EXISTS ${_db_ident};")
|
||||
endfunction()
|
||||
|
||||
function(cppbessot_test_pgsql_create_database connstr)
|
||||
cppbessot_test_require_real_pgsql_support()
|
||||
cppbessot_test_pgsql_admin_connstr(_admin_connstr)
|
||||
cppbessot_test_pgsql_connstr_dbname(_dbname "${connstr}")
|
||||
cppbessot_test_pgsql_escape_identifier(_db_ident "${_dbname}")
|
||||
cppbessot_test_pgsql_exec(
|
||||
"${_admin_connstr}"
|
||||
"CREATE DATABASE ${_db_ident};")
|
||||
endfunction()
|
||||
|
||||
function(cppbessot_test_pgsql_reset_database connstr)
|
||||
cppbessot_test_pgsql_drop_database("${connstr}")
|
||||
cppbessot_test_pgsql_create_database("${connstr}")
|
||||
endfunction()
|
||||
|
||||
function(cppbessot_test_pgsql_clone_command out_var source_connstr target_connstr)
|
||||
cppbessot_test_require_real_pgsql_support()
|
||||
cppbessot_test_require_var(CPPBESSOT_TEST_BINARY_DIR)
|
||||
cppbessot_test_require_var(CPPBESSOT_TEST_NAME)
|
||||
cppbessot_test_pgsql_admin_connstr(_admin_connstr)
|
||||
cppbessot_test_pgsql_connstr_dbname(_source_db "${source_connstr}")
|
||||
cppbessot_test_pgsql_connstr_dbname(_target_db "${target_connstr}")
|
||||
cppbessot_test_pgsql_connstr_field(_admin_host "${_admin_connstr}" "host")
|
||||
cppbessot_test_pgsql_connstr_field(_admin_port "${_admin_connstr}" "port")
|
||||
cppbessot_test_pgsql_connstr_field(_admin_user "${_admin_connstr}" "user")
|
||||
cppbessot_test_pgsql_connstr_field(_admin_password "${_admin_connstr}" "password")
|
||||
cppbessot_test_pgsql_connstr_field(_source_host "${source_connstr}" "host")
|
||||
cppbessot_test_pgsql_connstr_field(_source_port "${source_connstr}" "port")
|
||||
cppbessot_test_pgsql_connstr_field(_source_user "${source_connstr}" "user")
|
||||
cppbessot_test_pgsql_connstr_field(_source_password "${source_connstr}" "password")
|
||||
cppbessot_test_pgsql_connstr_field(_target_host "${target_connstr}" "host")
|
||||
cppbessot_test_pgsql_connstr_field(_target_port "${target_connstr}" "port")
|
||||
cppbessot_test_pgsql_connstr_field(_target_user "${target_connstr}" "user")
|
||||
cppbessot_test_pgsql_connstr_field(_target_password "${target_connstr}" "password")
|
||||
set(_script_path "${CPPBESSOT_TEST_BINARY_DIR}/cases/${CPPBESSOT_TEST_NAME}/pgsql-clone.sh")
|
||||
cppbessot_test_write_shell_script(
|
||||
"${_script_path}"
|
||||
"#!/usr/bin/env bash\n"
|
||||
"set -euo pipefail\n"
|
||||
"PGPASSWORD=\"" "${_admin_password}" "\" "
|
||||
"psql -h \"" "${_admin_host}" "\" -p \"" "${_admin_port}" "\" -U \"" "${_admin_user}" "\" -d postgres -v ON_ERROR_STOP=1 -c "
|
||||
"\"SELECT pg_terminate_backend(pid) FROM pg_stat_activity WHERE datname = '${_target_db}' AND pid <> pg_backend_pid()\"\n"
|
||||
"PGPASSWORD=\"" "${_admin_password}" "\" "
|
||||
"psql -h \"" "${_admin_host}" "\" -p \"" "${_admin_port}" "\" -U \"" "${_admin_user}" "\" -d postgres -v ON_ERROR_STOP=1 -c "
|
||||
"\"DROP DATABASE IF EXISTS ${_target_db}\"\n"
|
||||
"PGPASSWORD=\"" "${_admin_password}" "\" "
|
||||
"psql -h \"" "${_admin_host}" "\" -p \"" "${_admin_port}" "\" -U \"" "${_admin_user}" "\" -d postgres -v ON_ERROR_STOP=1 -c "
|
||||
"\"CREATE DATABASE ${_target_db} OWNER ${_target_user}\"\n"
|
||||
"PGPASSWORD=\"" "${_source_password}" "\" "
|
||||
"pg_dump -h \"" "${_source_host}" "\" -p \"" "${_source_port}" "\" -U \"" "${_source_user}" "\" -d \"" "${_source_db}" "\" --no-owner --no-privileges"
|
||||
" | "
|
||||
"PGPASSWORD=\"" "${_target_password}" "\" "
|
||||
"psql -h \"" "${_target_host}" "\" -p \"" "${_target_port}" "\" -U \"" "${_target_user}" "\" -d \"" "${_target_db}" "\" -v ON_ERROR_STOP=1\n")
|
||||
set(${out_var} "${_script_path}" PARENT_SCOPE)
|
||||
endfunction()
|
||||
|
||||
function(cppbessot_test_sqlite_exec db_path sql_text)
|
||||
find_program(_sqlite3 sqlite3 REQUIRED)
|
||||
get_filename_component(_parent "${db_path}" DIRECTORY)
|
||||
if(NOT "${_parent}" STREQUAL "")
|
||||
file(MAKE_DIRECTORY "${_parent}")
|
||||
endif()
|
||||
set(_sql_file "${CMAKE_CURRENT_BINARY_DIR}/cppbessot-test-sqlite-exec.sql")
|
||||
file(WRITE "${_sql_file}" "${sql_text}")
|
||||
string(REPLACE "\"" "\\\"" _sqlite_read_file "${_sql_file}")
|
||||
execute_process(
|
||||
COMMAND "${_sqlite3}" "${db_path}" ".read \"${_sqlite_read_file}\""
|
||||
RESULT_VARIABLE _result
|
||||
ERROR_VARIABLE _stderr
|
||||
)
|
||||
file(REMOVE "${_sql_file}")
|
||||
if(NOT _result EQUAL 0)
|
||||
message(FATAL_ERROR "Failed to execute SQLite SQL.\n${_stderr}")
|
||||
endif()
|
||||
endfunction()
|
||||
|
||||
function(cppbessot_test_sqlite_query_scalar out_var db_path query)
|
||||
find_program(_sqlite3 sqlite3 REQUIRED)
|
||||
execute_process(
|
||||
COMMAND "${_sqlite3}" -batch -noheader "${db_path}" "${query}"
|
||||
RESULT_VARIABLE _result
|
||||
OUTPUT_VARIABLE _stdout
|
||||
ERROR_VARIABLE _stderr
|
||||
OUTPUT_STRIP_TRAILING_WHITESPACE
|
||||
)
|
||||
if(NOT _result EQUAL 0)
|
||||
message(FATAL_ERROR "Failed SQLite query.\n${_stderr}")
|
||||
endif()
|
||||
set(${out_var} "${_stdout}" PARENT_SCOPE)
|
||||
endfunction()
|
||||
|
||||
function(cppbessot_test_write_mock_psql path)
|
||||
cppbessot_test_write_shell_script(
|
||||
"${path}"
|
||||
"#!/bin/sh\n"
|
||||
"set -eu\n"
|
||||
"log_file=\"${CPPBESSOT_TEST_LOG}\"\n"
|
||||
"conn=\"$1\"\n"
|
||||
"shift\n"
|
||||
"printf 'conn:%s\\n' \"$conn\" >> \"$log_file\"\n"
|
||||
"if [ \"\${CPPBESSOT_TEST_PSQL_FAIL_SELECT:-0}\" = \"1\" ]\n"
|
||||
"then\n"
|
||||
" if printf '%s ' \"$@\" | grep -F \"SELECT 1\" >/dev/null 2>&1\n"
|
||||
" then\n"
|
||||
" echo 'simulated select failure' >&2\n"
|
||||
" exit 1\n"
|
||||
" fi\n"
|
||||
"fi\n"
|
||||
"if [ \"\${CPPBESSOT_TEST_PSQL_FAIL_ALL:-0}\" = \"1\" ]\n"
|
||||
"then\n"
|
||||
" echo 'simulated psql failure' >&2\n"
|
||||
" exit 1\n"
|
||||
"fi\n"
|
||||
"while [ \"$#\" -gt 0 ]\n"
|
||||
"do\n"
|
||||
" if [ \"$1\" = \"-c\" ]\n"
|
||||
" then\n"
|
||||
" shift\n"
|
||||
" printf 'sqlcmd:%s\\n' \"$1\" >> \"$log_file\"\n"
|
||||
" elif [ \"$1\" = \"-f\" ]\n"
|
||||
" then\n"
|
||||
" shift\n"
|
||||
" printf 'sqlfile:%s\\n' \"$(basename \"$1\")\" >> \"$log_file\"\n"
|
||||
" fi\n"
|
||||
" shift\n"
|
||||
"done\n")
|
||||
endfunction()
|
||||
|
||||
function(cppbessot_test_assert_log_order log_path)
|
||||
file(READ "${log_path}" _contents)
|
||||
set(_cursor -1)
|
||||
foreach(_needle IN LISTS ARGN)
|
||||
string(FIND "${_contents}" "${_needle}" _index)
|
||||
if(_index EQUAL -1)
|
||||
message(FATAL_ERROR "Missing log entry `${_needle}`.\nLog contents:\n${_contents}")
|
||||
endif()
|
||||
if(_index LESS_EQUAL _cursor)
|
||||
message(FATAL_ERROR "Log entry `${_needle}` appeared out of order.\nLog contents:\n${_contents}")
|
||||
endif()
|
||||
set(_cursor "${_index}")
|
||||
endforeach()
|
||||
endfunction()
|
||||
@@ -0,0 +1,15 @@
|
||||
include("${CMAKE_CURRENT_LIST_DIR}/../cmake/TestCommon.cmake")
|
||||
|
||||
cppbessot_test_case_dir(_case_dir)
|
||||
cppbessot_test_write_project(
|
||||
"${_case_dir}"
|
||||
"set(CPPBESSOT_DB_SQLITE_DEV_PATH \"${_case_dir}/live/dev.sqlite\" CACHE STRING \"\")\n"
|
||||
"set(CPPBESSOT_DB_PGSQL_DEV_CONNSTR \"dbname=test\" CACHE STRING \"\")\n")
|
||||
cppbessot_test_add_schema("${_case_dir}" "v1.1")
|
||||
cppbessot_test_add_sql_file("${_case_dir}/db/v1.1/generated-sql-ddl/sqlite/01-schema.sql"
|
||||
"CREATE TABLE sample(id TEXT);\n")
|
||||
|
||||
cppbessot_test_configure_project("${_case_dir}" "${_case_dir}/build" _cfg_result _cfg_stdout _cfg_stderr)
|
||||
cppbessot_test_assert_success("${_cfg_result}" "${_cfg_stderr}" "fixture configure")
|
||||
cppbessot_test_build_target("${_case_dir}/build" "db_createfrom" _build_result _build_stdout _build_stderr)
|
||||
cppbessot_test_assert_failure_contains("${_build_result}" "${_build_stderr}" "is ambiguous")
|
||||
@@ -0,0 +1,28 @@
|
||||
include("${CMAKE_CURRENT_LIST_DIR}/../cmake/TestCommon.cmake")
|
||||
|
||||
cppbessot_test_case_dir(_case_dir)
|
||||
set(_log_file "${_case_dir}/events.log")
|
||||
cppbessot_test_write_file("${_log_file}" "")
|
||||
|
||||
cppbessot_test_write_project(
|
||||
"${_case_dir}"
|
||||
"set(DB_MIGRATE_WITH \"v1.0-v1.1\" CACHE STRING \"\")\n"
|
||||
"set(CPPBESSOT_DB_SQLITE_DEV_PATH \"${_case_dir}/live/dev.sqlite\" CACHE STRING \"\")\n")
|
||||
cppbessot_test_add_schema("${_case_dir}" "v1.1")
|
||||
cppbessot_test_write_shell_script(
|
||||
"${_case_dir}/db/migrations/v1.0-v1.1/pre-structural-backfill.sh"
|
||||
"#!/bin/sh\n"
|
||||
"set -eu\n"
|
||||
"printf 'pre:%s:%s:%s:%s\\n' \"$CPPBESSOT_DB_TARGET\" \"$CPPBESSOT_DB_BACKEND\" \"$CPPBESSOT_DB_MIGRATE_WITH\" \"$CPPBESSOT_DB_SCHEMA_DIR_TO_GENERATE\" >> \"${_log_file}\"\n"
|
||||
"printf 'sqlite:%s\\n' \"$CPPBESSOT_DB_SQLITE_PATH\" >> \"${_log_file}\"\n")
|
||||
cppbessot_test_write_shell_script(
|
||||
"${_case_dir}/db/migrations/v1.0-v1.1/post-structural-backfill.sh"
|
||||
"#!/bin/sh\n"
|
||||
"set -eu\n"
|
||||
"printf 'post:%s\\n' \"$CPPBESSOT_DB_CREATEFROM_SCHEMA_DIR\" >> \"${_log_file}\"\n")
|
||||
|
||||
cppbessot_test_configure_project("${_case_dir}" "${_case_dir}/build" _cfg_result _cfg_stdout _cfg_stderr)
|
||||
cppbessot_test_assert_success("${_cfg_result}" "${_cfg_stderr}" "fixture configure")
|
||||
cppbessot_test_build_target("${_case_dir}/build" "db_migrate" _build_result _build_stdout _build_stderr)
|
||||
cppbessot_test_assert_success("${_build_result}" "${_build_stderr}" "db_migrate hook-only")
|
||||
cppbessot_test_assert_log_order("${_log_file}" "pre:dev:sqlite:v1.0-v1.1:v1.1" "sqlite:${_case_dir}/live/dev.sqlite" "post:v1.1")
|
||||
@@ -0,0 +1,15 @@
|
||||
include("${CMAKE_CURRENT_LIST_DIR}/../cmake/TestCommon.cmake")
|
||||
|
||||
cppbessot_test_case_dir(_case_dir)
|
||||
cppbessot_test_write_project(
|
||||
"${_case_dir}"
|
||||
"set(DB_TARGET \"bogus\" CACHE STRING \"\")\n"
|
||||
"set(CPPBESSOT_DB_SQLITE_DEV_PATH \"${_case_dir}/live/dev.sqlite\" CACHE STRING \"\")\n")
|
||||
cppbessot_test_add_schema("${_case_dir}" "v1.1")
|
||||
cppbessot_test_add_sql_file("${_case_dir}/db/v1.1/generated-sql-ddl/sqlite/01-schema.sql"
|
||||
"CREATE TABLE sample(id TEXT);\n")
|
||||
|
||||
cppbessot_test_configure_project("${_case_dir}" "${_case_dir}/build" _cfg_result _cfg_stdout _cfg_stderr)
|
||||
cppbessot_test_assert_success("${_cfg_result}" "${_cfg_stderr}" "fixture configure")
|
||||
cppbessot_test_build_target("${_case_dir}/build" "db_createfrom" _build_result _build_stdout _build_stderr)
|
||||
cppbessot_test_assert_failure_contains("${_build_result}" "${_build_stderr}" "DB_TARGET must be one of")
|
||||
@@ -0,0 +1,204 @@
|
||||
include("${CMAKE_CURRENT_LIST_DIR}/../cmake/TestCommon.cmake")
|
||||
|
||||
cppbessot_test_require_var(CPPBESSOT_TEST_BINARY_DIR)
|
||||
cppbessot_test_require_var(CPPBESSOT_TEST_MODULE_SOURCE_DIR)
|
||||
|
||||
set(_script_under_test "${CPPBESSOT_TEST_MODULE_SOURCE_DIR}/cmake/scripts/run_odb_migrations.cmake")
|
||||
|
||||
function(_cppbessot_migration_policy_fixture out_root)
|
||||
cppbessot_test_case_dir(_case_dir)
|
||||
set(_root "${_case_dir}/${ARGV1}")
|
||||
cppbessot_test_reset_dir("${_root}")
|
||||
file(MAKE_DIRECTORY
|
||||
"${_root}/from/generated-odb-source/sqlite"
|
||||
"${_root}/from/generated-odb-source/postgre"
|
||||
"${_root}/to/generated-cpp-source/include/cppbessot/model"
|
||||
"${_root}/to/generated-odb-source/sqlite"
|
||||
"${_root}/to/generated-odb-source/postgre"
|
||||
"${_root}/migrations")
|
||||
cppbessot_test_write_file(
|
||||
"${_root}/to/generated-cpp-source/include/cppbessot/model/Agent.h"
|
||||
"#pragma db object\n"
|
||||
"class Agent {};\n")
|
||||
cppbessot_test_write_file(
|
||||
"${_root}/to/generated-cpp-source/include/cppbessot/model/AgentPasswordHashType.h"
|
||||
"enum class AgentPasswordHashType { argon2id };\n")
|
||||
set(${out_root} "${_root}" PARENT_SCOPE)
|
||||
endfunction()
|
||||
|
||||
function(_cppbessot_migration_policy_run root odb_executable result_var stdout_var stderr_var)
|
||||
set(_args
|
||||
"-DCPPBESSOT_ODB_EXECUTABLE=${odb_executable}"
|
||||
"-DCPPBESSOT_FROM_VERSION_DIR=${root}/from"
|
||||
"-DCPPBESSOT_TO_VERSION_DIR=${root}/to"
|
||||
"-DCPPBESSOT_MIGRATION_DIR=${root}/migrations")
|
||||
foreach(_extra_arg IN LISTS ARGN)
|
||||
list(APPEND _args "${_extra_arg}")
|
||||
endforeach()
|
||||
|
||||
execute_process(
|
||||
COMMAND "${CMAKE_COMMAND}" ${_args} -P "${_script_under_test}"
|
||||
RESULT_VARIABLE _result
|
||||
OUTPUT_VARIABLE _stdout
|
||||
ERROR_VARIABLE _stderr)
|
||||
|
||||
set(${result_var} "${_result}" PARENT_SCOPE)
|
||||
set(${stdout_var} "${_stdout}" PARENT_SCOPE)
|
||||
set(${stderr_var} "${_stderr}" PARENT_SCOPE)
|
||||
endfunction()
|
||||
|
||||
function(_cppbessot_migration_policy_true_tool out_tool log_path)
|
||||
get_filename_component(_tool_dir "${log_path}" DIRECTORY)
|
||||
cppbessot_test_write_shell_script(
|
||||
"${_tool_dir}/odb-true.sh"
|
||||
"#!/usr/bin/env bash\n"
|
||||
"printf '%s\\n' \"$*\" >> \"${log_path}\"\n")
|
||||
set(${out_tool} "${_tool_dir}/odb-true.sh" PARENT_SCOPE)
|
||||
endfunction()
|
||||
|
||||
function(_cppbessot_migration_policy_retry_tool out_tool tool_dir)
|
||||
cppbessot_test_write_shell_script(
|
||||
"${tool_dir}/odb-retry.sh"
|
||||
"#!/usr/bin/env bash\n"
|
||||
"while [[ $# -gt 0 ]]; do\n"
|
||||
" if [[ \"$1\" == \"--changelog-in\" ]]; then\n"
|
||||
" changelog=\"$2\"\n"
|
||||
" shift 2\n"
|
||||
" else\n"
|
||||
" shift\n"
|
||||
" fi\n"
|
||||
"done\n"
|
||||
"if [[ \"$changelog\" == *\".empty-baseline\"* ]]; then\n"
|
||||
" exit 0\n"
|
||||
"fi\n"
|
||||
"echo \"error: SQLite does not support dropping of columns\" >&2\n"
|
||||
"exit 1\n")
|
||||
set(${out_tool} "${tool_dir}/odb-retry.sh" PARENT_SCOPE)
|
||||
endfunction()
|
||||
|
||||
function(_cppbessot_migration_policy_false_tool out_tool tool_dir)
|
||||
cppbessot_test_write_shell_script(
|
||||
"${tool_dir}/odb-false.sh"
|
||||
"#!/usr/bin/env bash\n"
|
||||
"echo \"error: unrelated ODB failure\" >&2\n"
|
||||
"exit 1\n")
|
||||
set(${out_tool} "${tool_dir}/odb-false.sh" PARENT_SCOPE)
|
||||
endfunction()
|
||||
|
||||
cppbessot_test_case_dir(_case_dir)
|
||||
set(_configure_root "${_case_dir}/configure-policy")
|
||||
set(_configure_build "${_case_dir}/configure-policy-build")
|
||||
cppbessot_test_reset_dir("${_configure_root}")
|
||||
cppbessot_test_write_file(
|
||||
"${_configure_root}/CMakeLists.txt"
|
||||
"cmake_minimum_required(VERSION 3.20)\n"
|
||||
"project(cppbessot_migration_policy_configure LANGUAGES CXX)\n"
|
||||
"set(CPPBESSOT_AUTO_ENABLE OFF CACHE BOOL \"\")\n"
|
||||
"include(\"${CPPBESSOT_TEST_MODULE_SOURCE_DIR}/cmake/CppBeSSOT.cmake\")\n")
|
||||
execute_process(
|
||||
COMMAND "${CMAKE_COMMAND}" -S "${_configure_root}" -B "${_configure_build}"
|
||||
RESULT_VARIABLE _result
|
||||
ERROR_VARIABLE _stderr)
|
||||
cppbessot_test_assert_success("${_result}" "${_stderr}" "migration backend default configure")
|
||||
file(READ "${_configure_build}/CMakeCache.txt" _cache_text)
|
||||
cppbessot_test_assert_contains(
|
||||
"${_cache_text}"
|
||||
"CPPBESSOT_GEN_MIGRATION_BACKENDS:STRING=sqlite;pgsql"
|
||||
"migration backend default cache")
|
||||
cppbessot_test_assert_contains(
|
||||
"${_cache_text}"
|
||||
"CPPBESSOT_GEN_MIGRATIONS_BACKENDS:STRING=sqlite;pgsql"
|
||||
"migration backend alias default cache")
|
||||
|
||||
execute_process(
|
||||
COMMAND "${CMAKE_COMMAND}" -S "${_configure_root}" -B "${_configure_build}" -DCPPBESSOT_GEN_MIGRATION_BACKENDS=pgsql
|
||||
RESULT_VARIABLE _result
|
||||
ERROR_VARIABLE _stderr)
|
||||
cppbessot_test_assert_success("${_result}" "${_stderr}" "migration backend override reconfigure")
|
||||
file(READ "${_configure_build}/CMakeCache.txt" _cache_text)
|
||||
cppbessot_test_assert_contains(
|
||||
"${_cache_text}"
|
||||
"CPPBESSOT_GEN_MIGRATION_BACKENDS:STRING=pgsql"
|
||||
"migration backend override cache")
|
||||
cppbessot_test_assert_contains(
|
||||
"${_cache_text}"
|
||||
"CPPBESSOT_GEN_MIGRATIONS_BACKENDS:STRING=pgsql"
|
||||
"migration backend alias override cache")
|
||||
|
||||
_cppbessot_migration_policy_fixture(_default_root "default")
|
||||
_cppbessot_migration_policy_true_tool(_true_tool "${_default_root}/odb.log")
|
||||
_cppbessot_migration_policy_run("${_default_root}" "${_true_tool}" _result _stdout _stderr)
|
||||
cppbessot_test_assert_success("${_result}" "${_stderr}" "default migration backend policy")
|
||||
cppbessot_test_assert_file_exists("${_default_root}/migrations/sqlite")
|
||||
cppbessot_test_assert_file_exists("${_default_root}/migrations/postgre")
|
||||
file(READ "${_default_root}/odb.log" _default_log)
|
||||
cppbessot_test_assert_contains("${_default_log}" "-d sqlite" "default backend log")
|
||||
cppbessot_test_assert_contains("${_default_log}" "-d pgsql" "default backend log")
|
||||
if(_default_log MATCHES "AgentPasswordHashType\\.h")
|
||||
message(FATAL_ERROR "Enum-only headers must not be passed to ODB migration generation.")
|
||||
endif()
|
||||
|
||||
_cppbessot_migration_policy_fixture(_pgsql_root "pgsql-only")
|
||||
_cppbessot_migration_policy_true_tool(_true_tool "${_pgsql_root}/odb.log")
|
||||
_cppbessot_migration_policy_run(
|
||||
"${_pgsql_root}"
|
||||
"${_true_tool}"
|
||||
_result
|
||||
_stdout
|
||||
_stderr
|
||||
"-DCPPBESSOT_GEN_MIGRATION_BACKENDS=pgsql")
|
||||
cppbessot_test_assert_success("${_result}" "${_stderr}" "pgsql-only migration backend policy")
|
||||
cppbessot_test_assert_file_exists("${_pgsql_root}/migrations/postgre")
|
||||
if(EXISTS "${_pgsql_root}/migrations/sqlite")
|
||||
message(FATAL_ERROR "pgsql-only backend selection unexpectedly created sqlite migration output.")
|
||||
endif()
|
||||
|
||||
_cppbessot_migration_policy_fixture(_plural_root "plural-alias")
|
||||
_cppbessot_migration_policy_true_tool(_true_tool "${_plural_root}/odb.log")
|
||||
_cppbessot_migration_policy_run(
|
||||
"${_plural_root}"
|
||||
"${_true_tool}"
|
||||
_result
|
||||
_stdout
|
||||
_stderr
|
||||
"-DCPPBESSOT_GEN_MIGRATIONS_BACKENDS=sqlite")
|
||||
cppbessot_test_assert_success("${_result}" "${_stderr}" "legacy plural backend alias policy")
|
||||
cppbessot_test_assert_file_exists("${_plural_root}/migrations/sqlite")
|
||||
if(EXISTS "${_plural_root}/migrations/postgre")
|
||||
message(FATAL_ERROR "plural alias sqlite-only backend selection unexpectedly created postgre migration output.")
|
||||
endif()
|
||||
|
||||
_cppbessot_migration_policy_fixture(_invalid_root "invalid")
|
||||
_cppbessot_migration_policy_run(
|
||||
"${_invalid_root}"
|
||||
"/bin/true"
|
||||
_result
|
||||
_stdout
|
||||
_stderr
|
||||
"-DCPPBESSOT_GEN_MIGRATION_BACKENDS=mysql")
|
||||
cppbessot_test_assert_failure_contains("${_result}" "${_stderr}" "Unsupported migration backend")
|
||||
|
||||
_cppbessot_migration_policy_fixture(_retry_root "sqlite-retry")
|
||||
file(WRITE "${_retry_root}/from/generated-odb-source/sqlite/Agent.xml" "")
|
||||
_cppbessot_migration_policy_retry_tool(_retry_tool "${_retry_root}")
|
||||
_cppbessot_migration_policy_run(
|
||||
"${_retry_root}"
|
||||
"${_retry_tool}"
|
||||
_result
|
||||
_stdout
|
||||
_stderr
|
||||
"-DCPPBESSOT_GEN_MIGRATION_BACKENDS=sqlite")
|
||||
cppbessot_test_assert_success("${_result}" "${_stderr}" "sqlite drop-column retry policy")
|
||||
cppbessot_test_assert_contains("${_stderr}" "SQLite incremental migration failed" "sqlite retry warning")
|
||||
|
||||
_cppbessot_migration_policy_fixture(_failure_root "sqlite-unrelated-failure")
|
||||
file(WRITE "${_failure_root}/from/generated-odb-source/sqlite/Agent.xml" "")
|
||||
_cppbessot_migration_policy_false_tool(_false_tool "${_failure_root}")
|
||||
_cppbessot_migration_policy_run(
|
||||
"${_failure_root}"
|
||||
"${_false_tool}"
|
||||
_result
|
||||
_stdout
|
||||
_stderr
|
||||
"-DCPPBESSOT_GEN_MIGRATION_BACKENDS=sqlite")
|
||||
cppbessot_test_assert_failure_contains("${_result}" "${_stderr}" "Migration generation failed")
|
||||
@@ -0,0 +1,12 @@
|
||||
include("${CMAKE_CURRENT_LIST_DIR}/../cmake/TestCommon.cmake")
|
||||
|
||||
cppbessot_test_case_dir(_case_dir)
|
||||
cppbessot_test_write_project("${_case_dir}" "")
|
||||
cppbessot_test_add_schema("${_case_dir}" "v1.1")
|
||||
cppbessot_test_add_sql_file("${_case_dir}/db/v1.1/generated-sql-ddl/sqlite/01-schema.sql"
|
||||
"CREATE TABLE sample(id TEXT);\n")
|
||||
|
||||
cppbessot_test_configure_project("${_case_dir}" "${_case_dir}/build" _cfg_result _cfg_stdout _cfg_stderr)
|
||||
cppbessot_test_assert_success("${_cfg_result}" "${_cfg_stderr}" "fixture configure")
|
||||
cppbessot_test_build_target("${_case_dir}/build" "db_createfrom" _build_result _build_stdout _build_stderr)
|
||||
cppbessot_test_assert_failure_contains("${_build_result}" "${_build_stderr}" "is not mapped")
|
||||
@@ -0,0 +1,12 @@
|
||||
include("${CMAKE_CURRENT_LIST_DIR}/../cmake/TestCommon.cmake")
|
||||
|
||||
cppbessot_test_case_dir(_case_dir)
|
||||
cppbessot_test_write_project(
|
||||
"${_case_dir}"
|
||||
"set(CPPBESSOT_DB_SQLITE_DEV_PATH \"${_case_dir}/live/dev.sqlite\" CACHE STRING \"\")\n")
|
||||
cppbessot_test_add_schema("${_case_dir}" "v1.1")
|
||||
|
||||
cppbessot_test_configure_project("${_case_dir}" "${_case_dir}/build" _cfg_result _cfg_stdout _cfg_stderr)
|
||||
cppbessot_test_assert_success("${_cfg_result}" "${_cfg_stderr}" "fixture configure")
|
||||
cppbessot_test_build_target("${_case_dir}/build" "db_migrate" _build_result _build_stdout _build_stderr)
|
||||
cppbessot_test_assert_failure_contains("${_build_result}" "${_build_stderr}" "Required variable `DB_MIGRATE_WITH` is missing")
|
||||
@@ -0,0 +1,13 @@
|
||||
include("${CMAKE_CURRENT_LIST_DIR}/../cmake/TestCommon.cmake")
|
||||
|
||||
cppbessot_test_case_dir(_case_dir)
|
||||
cppbessot_test_write_project(
|
||||
"${_case_dir}"
|
||||
"set(DB_MIGRATE_WITH \"v1.0-v1.1\" CACHE STRING \"\")\n"
|
||||
"set(CPPBESSOT_DB_SQLITE_DEV_PATH \"${_case_dir}/live/dev.sqlite\" CACHE STRING \"\")\n")
|
||||
cppbessot_test_add_schema("${_case_dir}" "v1.1")
|
||||
|
||||
cppbessot_test_configure_project("${_case_dir}" "${_case_dir}/build" _cfg_result _cfg_stdout _cfg_stderr)
|
||||
cppbessot_test_assert_success("${_cfg_result}" "${_cfg_stderr}" "fixture configure")
|
||||
cppbessot_test_build_target("${_case_dir}/build" "db_migrate" _build_result _build_stdout _build_stderr)
|
||||
cppbessot_test_assert_failure_contains("${_build_result}" "${_build_stderr}" "Migration directory does not exist")
|
||||
@@ -0,0 +1,131 @@
|
||||
include("${CMAKE_CURRENT_LIST_DIR}/../cmake/TestCommon.cmake")
|
||||
|
||||
cppbessot_test_require_var(CPPBESSOT_TEST_BINARY_DIR)
|
||||
cppbessot_test_require_var(CPPBESSOT_TEST_MODULE_SOURCE_DIR)
|
||||
|
||||
cppbessot_test_case_dir(_case_dir)
|
||||
set(_fixture_root "${_case_dir}/openapi-filter")
|
||||
cppbessot_test_reset_dir("${_fixture_root}")
|
||||
file(MAKE_DIRECTORY "${_fixture_root}/db/v1.test/openapi")
|
||||
cppbessot_test_write_file(
|
||||
"${_fixture_root}/db/v1.test/openapi/openapi.yaml"
|
||||
"openapi: 3.0.3\n"
|
||||
"info:\n"
|
||||
" title: cppbessot-openapi-filter-test\n"
|
||||
" version: \"1.0.0\"\n"
|
||||
"paths: {}\n"
|
||||
"components:\n"
|
||||
" schemas:\n"
|
||||
" AgentPasswordHashType:\n"
|
||||
" type: integer\n"
|
||||
" format: int32\n"
|
||||
" enum:\n"
|
||||
" - 0\n"
|
||||
" CredentialType:\n"
|
||||
" type: integer\n"
|
||||
" format: int32\n"
|
||||
" enum:\n"
|
||||
" - 0\n"
|
||||
" Agents:\n"
|
||||
" type: object\n"
|
||||
" x-odbTable: Agents\n"
|
||||
" properties:\n"
|
||||
" id:\n"
|
||||
" type: string\n"
|
||||
" Roles:\n"
|
||||
" type: object\n"
|
||||
" x-odbTable: Roles\n"
|
||||
" properties:\n"
|
||||
" id:\n"
|
||||
" type: string\n")
|
||||
|
||||
set(_module_cmake_dir "${CPPBESSOT_TEST_MODULE_SOURCE_DIR}/cmake")
|
||||
set(PROJECT_SOURCE_DIR "${_fixture_root}")
|
||||
set(CPPBESSOT_WORKDIR "db")
|
||||
include("${_module_cmake_dir}/dbGenerationCommon.cmake")
|
||||
|
||||
cppbessot_get_openapi_schema_names(_all_schema_names "v1.test")
|
||||
cppbessot_get_openapi_odb_table_schema_names(_odb_table_schema_names "v1.test")
|
||||
cppbessot_get_expected_odb_outputs(
|
||||
_sqlite_odb_sources
|
||||
_pgsql_odb_sources
|
||||
"v1.test")
|
||||
cppbessot_get_expected_odb_model_headers(_odb_model_headers "v1.test")
|
||||
cppbessot_get_expected_odb_outputs_all(_all_odb_sources "v1.test")
|
||||
cppbessot_get_expected_odb_generation_artifacts(
|
||||
_generation_model_headers
|
||||
_generation_all_odb_sources
|
||||
"v1.test")
|
||||
|
||||
set(_expected_all_schema_names
|
||||
AgentPasswordHashType
|
||||
CredentialType
|
||||
Agents
|
||||
Roles)
|
||||
set(_expected_odb_table_schema_names
|
||||
Agents
|
||||
Roles)
|
||||
|
||||
function(_cppbessot_assert_same_list label actual expected)
|
||||
list(SORT actual)
|
||||
list(SORT expected)
|
||||
if(NOT "${actual}" STREQUAL "${expected}")
|
||||
message(FATAL_ERROR
|
||||
"${label} mismatch.\n"
|
||||
" expected: ${expected}\n"
|
||||
" actual: ${actual}")
|
||||
endif()
|
||||
endfunction()
|
||||
|
||||
_cppbessot_assert_same_list(
|
||||
"all schema names"
|
||||
"${_all_schema_names}"
|
||||
"${_expected_all_schema_names}")
|
||||
_cppbessot_assert_same_list(
|
||||
"x-odbTable schema names"
|
||||
"${_odb_table_schema_names}"
|
||||
"${_expected_odb_table_schema_names}")
|
||||
_cppbessot_assert_same_list(
|
||||
"generation artifact model headers"
|
||||
"${_generation_model_headers}"
|
||||
"${_odb_model_headers}")
|
||||
_cppbessot_assert_same_list(
|
||||
"generation artifact ODB sources"
|
||||
"${_generation_all_odb_sources}"
|
||||
"${_all_odb_sources}")
|
||||
|
||||
list(LENGTH _sqlite_odb_sources _sqlite_count)
|
||||
list(LENGTH _pgsql_odb_sources _pgsql_count)
|
||||
list(LENGTH _odb_model_headers _header_count)
|
||||
list(LENGTH _all_odb_sources _all_odb_count)
|
||||
|
||||
if(NOT _sqlite_count EQUAL 2)
|
||||
message(FATAL_ERROR
|
||||
"Expected 2 sqlite ODB sources, got ${_sqlite_count}: ${_sqlite_odb_sources}")
|
||||
endif()
|
||||
if(NOT _pgsql_count EQUAL 2)
|
||||
message(FATAL_ERROR
|
||||
"Expected 2 postgre ODB sources, got ${_pgsql_count}: ${_pgsql_odb_sources}")
|
||||
endif()
|
||||
if(NOT _header_count EQUAL 2)
|
||||
message(FATAL_ERROR
|
||||
"Expected 2 ODB model headers, got ${_header_count}: ${_odb_model_headers}")
|
||||
endif()
|
||||
if(NOT _all_odb_count EQUAL 4)
|
||||
message(FATAL_ERROR
|
||||
"Expected 4 combined ODB sources, got ${_all_odb_count}: ${_all_odb_sources}")
|
||||
endif()
|
||||
|
||||
foreach(_forbidden IN ITEMS
|
||||
AgentPasswordHashType
|
||||
CredentialType)
|
||||
foreach(_backend IN ITEMS sqlite postgre)
|
||||
set(_forbidden_source
|
||||
"${_fixture_root}/db/v1.test/generated-odb-source/${_backend}/${_forbidden}-odb.cxx")
|
||||
list(FIND _all_odb_sources "${_forbidden_source}" _forbidden_index)
|
||||
if(NOT _forbidden_index EQUAL -1)
|
||||
message(FATAL_ERROR
|
||||
"Enum schema `${_forbidden}` must not produce `${_forbidden_source}`.")
|
||||
endif()
|
||||
endforeach()
|
||||
endforeach()
|
||||
@@ -0,0 +1,49 @@
|
||||
include("${CMAKE_CURRENT_LIST_DIR}/../cmake/TestCommon.cmake")
|
||||
|
||||
cppbessot_test_require_real_pgsql_support()
|
||||
cppbessot_test_require_var(CPPBESSOT_DB_PGSQL_PRODDEV_CONNSTR)
|
||||
cppbessot_test_case_dir(_case_dir)
|
||||
set(_log_file "${_case_dir}/events.log")
|
||||
cppbessot_test_write_file("${_log_file}" "")
|
||||
cppbessot_test_pgsql_isolated_connstr(_dev_connstr "${CPPBESSOT_DB_PGSQL_PRODDEV_CONNSTR}" "dev")
|
||||
|
||||
cppbessot_test_pgsql_reset_database("${_dev_connstr}")
|
||||
cppbessot_test_pgsql_exec(
|
||||
"${_dev_connstr}"
|
||||
"CREATE TABLE sample(id TEXT PRIMARY KEY); INSERT INTO sample(id) VALUES ('row-1');")
|
||||
|
||||
cppbessot_test_cache_string_setting(_dev_setting "CPPBESSOT_DB_PGSQL_DEV_CONNSTR" "${_dev_connstr}")
|
||||
cppbessot_test_write_project(
|
||||
"${_case_dir}"
|
||||
"set(DB_MIGRATE_WITH \"v1.0-v1.1\" CACHE STRING \"\")\n"
|
||||
"${_dev_setting}")
|
||||
cppbessot_test_add_schema("${_case_dir}" "v1.1")
|
||||
cppbessot_test_write_shell_script(
|
||||
"${_case_dir}/db/migrations/v1.0-v1.1/pre-structural-backfill.sh"
|
||||
"#!/bin/sh\n"
|
||||
"set -eu\n"
|
||||
"printf 'pre:%s:%s:%s\\n' \"$CPPBESSOT_DB_TARGET\" \"$CPPBESSOT_DB_BACKEND\" \"$CPPBESSOT_DB_PGSQL_CONNSTR\" >> \"${_log_file}\"\n")
|
||||
cppbessot_test_write_shell_script(
|
||||
"${_case_dir}/db/migrations/v1.0-v1.1/post-structural-backfill.sh"
|
||||
"#!/bin/sh\n"
|
||||
"set -eu\n"
|
||||
"printf 'post:%s:%s\\n' \"$CPPBESSOT_DB_MIGRATE_WITH\" \"$CPPBESSOT_DB_SCHEMA_DIR_TO_GENERATE\" >> \"${_log_file}\"\n")
|
||||
cppbessot_test_add_sql_file("${_case_dir}/db/migrations/v1.0-v1.1/postgre/01-migrate.sql"
|
||||
"ALTER TABLE sample ADD COLUMN note TEXT;\n"
|
||||
"UPDATE sample SET note = 'hooked' WHERE id = 'row-1';\n")
|
||||
|
||||
cppbessot_test_configure_project("${_case_dir}" "${_case_dir}/build" _cfg_result _cfg_stdout _cfg_stderr)
|
||||
cppbessot_test_assert_success("${_cfg_result}" "${_cfg_stderr}" "fixture configure")
|
||||
cppbessot_test_build_target("${_case_dir}/build" "db_migrate" _build_result _build_stdout _build_stderr)
|
||||
cppbessot_test_assert_success("${_build_result}" "${_build_stderr}" "db_migrate pgsql backfill real")
|
||||
|
||||
cppbessot_test_assert_log_order(
|
||||
"${_log_file}"
|
||||
"pre:dev:postgre:${_dev_connstr}"
|
||||
"post:v1.0-v1.1:v1.1")
|
||||
cppbessot_test_pgsql_query_scalar(_note "${_dev_connstr}" "SELECT note FROM sample WHERE id='row-1';")
|
||||
if(NOT "${_note}" STREQUAL "hooked")
|
||||
message(FATAL_ERROR "Expected PostgreSQL backfill migration to update note column, got `${_note}`.")
|
||||
endif()
|
||||
|
||||
cppbessot_test_pgsql_drop_database("${_dev_connstr}")
|
||||
@@ -0,0 +1,31 @@
|
||||
include("${CMAKE_CURRENT_LIST_DIR}/../cmake/TestCommon.cmake")
|
||||
|
||||
cppbessot_test_case_dir(_case_dir)
|
||||
set(_tool_dir "${_case_dir}/tools")
|
||||
set(_log_file "${_case_dir}/psql.log")
|
||||
set(CPPBESSOT_TEST_LOG "${_log_file}")
|
||||
cppbessot_test_write_file("${_log_file}" "")
|
||||
cppbessot_test_write_mock_psql("${_tool_dir}/psql")
|
||||
cppbessot_test_set_path_with_tool_dir("${_tool_dir}")
|
||||
set(ENV{CPPBESSOT_TEST_LOG} "${_log_file}")
|
||||
set(ENV{CPPBESSOT_TEST_PSQL_FAIL_ALL} "0")
|
||||
set(ENV{CPPBESSOT_TEST_PSQL_FAIL_SELECT} "0")
|
||||
|
||||
cppbessot_test_write_project(
|
||||
"${_case_dir}"
|
||||
"set(CPPBESSOT_DB_PGSQL_DEV_CONNSTR \"dbname=dev_db\" CACHE STRING \"\")\n")
|
||||
cppbessot_test_add_schema("${_case_dir}" "v1.1")
|
||||
cppbessot_test_add_sql_file("${_case_dir}/db/v1.1/generated-sql-ddl/postgre/01-schema.sql"
|
||||
"CREATE TABLE sample(id TEXT PRIMARY KEY);\n")
|
||||
cppbessot_test_add_sql_file("${_case_dir}/db/v1.1/generated-sql-ddl/postgre/02-seed.sql"
|
||||
"INSERT INTO sample(id) VALUES ('row-1');\n")
|
||||
|
||||
cppbessot_test_configure_project("${_case_dir}" "${_case_dir}/build" _cfg_result _cfg_stdout _cfg_stderr)
|
||||
cppbessot_test_assert_success("${_cfg_result}" "${_cfg_stderr}" "fixture configure")
|
||||
cppbessot_test_build_target("${_case_dir}/build" "db_createfrom" _build_result _build_stdout _build_stderr)
|
||||
cppbessot_test_assert_success("${_build_result}" "${_build_stderr}" "db_createfrom pgsql mock")
|
||||
|
||||
file(READ "${_log_file}" _log_contents)
|
||||
cppbessot_test_assert_contains("${_log_contents}" "sqlcmd:DROP SCHEMA IF EXISTS public CASCADE; CREATE SCHEMA public;" "pgsql reset log")
|
||||
cppbessot_test_assert_contains("${_log_contents}" "sqlfile:01-schema.sql" "pgsql schema log")
|
||||
cppbessot_test_assert_contains("${_log_contents}" "sqlfile:02-seed.sql" "pgsql seed log")
|
||||
@@ -0,0 +1,38 @@
|
||||
include("${CMAKE_CURRENT_LIST_DIR}/../cmake/TestCommon.cmake")
|
||||
|
||||
cppbessot_test_require_real_pgsql_support()
|
||||
cppbessot_test_require_var(CPPBESSOT_DB_PGSQL_PRODDEV_CONNSTR)
|
||||
cppbessot_test_case_dir(_case_dir)
|
||||
cppbessot_test_pgsql_isolated_connstr(_dev_connstr "${CPPBESSOT_DB_PGSQL_PRODDEV_CONNSTR}" "dev")
|
||||
|
||||
cppbessot_test_pgsql_reset_database("${_dev_connstr}")
|
||||
cppbessot_test_pgsql_exec(
|
||||
"${_dev_connstr}"
|
||||
"CREATE TABLE legacy_data(id TEXT PRIMARY KEY); INSERT INTO legacy_data(id) VALUES ('old-row');")
|
||||
|
||||
cppbessot_test_cache_string_setting(_dev_setting "CPPBESSOT_DB_PGSQL_DEV_CONNSTR" "${_dev_connstr}")
|
||||
cppbessot_test_write_project("${_case_dir}" "${_dev_setting}")
|
||||
cppbessot_test_add_schema("${_case_dir}" "v1.1")
|
||||
cppbessot_test_add_sql_file("${_case_dir}/db/v1.1/generated-sql-ddl/postgre/01-schema.sql"
|
||||
"CREATE TABLE sample(id TEXT PRIMARY KEY, note TEXT);\n")
|
||||
cppbessot_test_add_sql_file("${_case_dir}/db/v1.1/generated-sql-ddl/postgre/02-seed.sql"
|
||||
"INSERT INTO sample(id, note) VALUES ('row-1', 'seeded');\n")
|
||||
|
||||
cppbessot_test_configure_project("${_case_dir}" "${_case_dir}/build" _cfg_result _cfg_stdout _cfg_stderr)
|
||||
cppbessot_test_assert_success("${_cfg_result}" "${_cfg_stderr}" "fixture configure")
|
||||
cppbessot_test_build_target("${_case_dir}/build" "db_createfrom" _build_result _build_stdout _build_stderr)
|
||||
cppbessot_test_assert_success("${_build_result}" "${_build_stderr}" "db_createfrom pgsql real")
|
||||
|
||||
cppbessot_test_pgsql_query_scalar(_row_count "${_dev_connstr}" "SELECT COUNT(*) FROM sample;")
|
||||
if(NOT "${_row_count}" STREQUAL "1")
|
||||
message(FATAL_ERROR "Expected seeded sample row after PostgreSQL createfrom, got `${_row_count}`.")
|
||||
endif()
|
||||
cppbessot_test_pgsql_query_scalar(
|
||||
_legacy_count
|
||||
"${_dev_connstr}"
|
||||
"SELECT COUNT(*) FROM information_schema.tables WHERE table_schema = 'public' AND table_name = 'legacy_data';")
|
||||
if(NOT "${_legacy_count}" STREQUAL "0")
|
||||
message(FATAL_ERROR "Expected legacy_data table to be removed during PostgreSQL createfrom.")
|
||||
endif()
|
||||
|
||||
cppbessot_test_pgsql_drop_database("${_dev_connstr}")
|
||||
@@ -0,0 +1,34 @@
|
||||
include("${CMAKE_CURRENT_LIST_DIR}/../cmake/TestCommon.cmake")
|
||||
|
||||
cppbessot_test_case_dir(_case_dir)
|
||||
set(_tool_dir "${_case_dir}/tools")
|
||||
set(_log_file "${_case_dir}/events.log")
|
||||
set(CPPBESSOT_TEST_LOG "${_log_file}")
|
||||
cppbessot_test_write_file("${_log_file}" "")
|
||||
cppbessot_test_write_mock_psql("${_tool_dir}/psql")
|
||||
cppbessot_test_set_path_with_tool_dir("${_tool_dir}")
|
||||
set(ENV{CPPBESSOT_TEST_LOG} "${_log_file}")
|
||||
set(ENV{CPPBESSOT_TEST_PSQL_FAIL_ALL} "0")
|
||||
set(ENV{CPPBESSOT_TEST_PSQL_FAIL_SELECT} "0")
|
||||
|
||||
cppbessot_test_write_project(
|
||||
"${_case_dir}"
|
||||
"set(DB_TARGET \"proddev\" CACHE STRING \"\")\n"
|
||||
"set(DB_MIGRATE_WITH \"v1.0-v1.1\" CACHE STRING \"\")\n"
|
||||
"set(CPPBESSOT_DB_PGSQL_PRODDEV_CONNSTR \"dbname=proddev_db\" CACHE STRING \"\")\n"
|
||||
"set(CPPBESSOT_DB_PGSQL_CLONE_PROD_TO_PRODDEV_COMMAND \"printf 'clone\\\\n' >> '${_log_file}'\" CACHE STRING \"\")\n")
|
||||
cppbessot_test_add_schema("${_case_dir}" "v1.1")
|
||||
cppbessot_test_write_shell_script(
|
||||
"${_case_dir}/db/migrations/v1.0-v1.1/pre-structural-backfill.sh"
|
||||
"#!/bin/sh\nset -eu\nprintf 'pre\\n' >> \"$CPPBESSOT_TEST_LOG\"\n")
|
||||
cppbessot_test_write_shell_script(
|
||||
"${_case_dir}/db/migrations/v1.0-v1.1/post-structural-backfill.sh"
|
||||
"#!/bin/sh\nset -eu\nprintf 'post\\n' >> \"$CPPBESSOT_TEST_LOG\"\n")
|
||||
cppbessot_test_add_sql_file("${_case_dir}/db/migrations/v1.0-v1.1/postgre/01-migrate.sql"
|
||||
"ALTER TABLE sample ADD COLUMN note TEXT;\n")
|
||||
|
||||
cppbessot_test_configure_project("${_case_dir}" "${_case_dir}/build" _cfg_result _cfg_stdout _cfg_stderr)
|
||||
cppbessot_test_assert_success("${_cfg_result}" "${_cfg_stderr}" "fixture configure")
|
||||
cppbessot_test_build_target("${_case_dir}/build" "db_migrate" _build_result _build_stdout _build_stderr)
|
||||
cppbessot_test_assert_success("${_build_result}" "${_build_stderr}" "db_migrate pgsql order")
|
||||
cppbessot_test_assert_log_order("${_log_file}" "clone" "pre" "sqlfile:01-migrate.sql" "post")
|
||||
@@ -0,0 +1,33 @@
|
||||
include("${CMAKE_CURRENT_LIST_DIR}/../cmake/TestCommon.cmake")
|
||||
|
||||
cppbessot_test_require_real_pgsql_support()
|
||||
cppbessot_test_require_var(CPPBESSOT_DB_PGSQL_PRODDEV_CONNSTR)
|
||||
cppbessot_test_case_dir(_case_dir)
|
||||
cppbessot_test_pgsql_isolated_connstr(_dev_connstr "${CPPBESSOT_DB_PGSQL_PRODDEV_CONNSTR}" "dev")
|
||||
|
||||
cppbessot_test_pgsql_reset_database("${_dev_connstr}")
|
||||
cppbessot_test_pgsql_exec(
|
||||
"${_dev_connstr}"
|
||||
"CREATE TABLE sample(id TEXT PRIMARY KEY); INSERT INTO sample(id) VALUES ('row-1');")
|
||||
|
||||
cppbessot_test_cache_string_setting(_dev_setting "CPPBESSOT_DB_PGSQL_DEV_CONNSTR" "${_dev_connstr}")
|
||||
cppbessot_test_write_project(
|
||||
"${_case_dir}"
|
||||
"set(DB_MIGRATE_WITH \"v1.0-v1.1\" CACHE STRING \"\")\n"
|
||||
"${_dev_setting}")
|
||||
cppbessot_test_add_schema("${_case_dir}" "v1.1")
|
||||
cppbessot_test_add_sql_file("${_case_dir}/db/migrations/v1.0-v1.1/postgre/01-migrate.sql"
|
||||
"ALTER TABLE sample ADD COLUMN note TEXT;\n"
|
||||
"UPDATE sample SET note = 'migrated' WHERE id = 'row-1';\n")
|
||||
|
||||
cppbessot_test_configure_project("${_case_dir}" "${_case_dir}/build" _cfg_result _cfg_stdout _cfg_stderr)
|
||||
cppbessot_test_assert_success("${_cfg_result}" "${_cfg_stderr}" "fixture configure")
|
||||
cppbessot_test_build_target("${_case_dir}/build" "db_migrate" _build_result _build_stdout _build_stderr)
|
||||
cppbessot_test_assert_success("${_build_result}" "${_build_stderr}" "db_migrate pgsql real")
|
||||
|
||||
cppbessot_test_pgsql_query_scalar(_note "${_dev_connstr}" "SELECT note FROM sample WHERE id='row-1';")
|
||||
if(NOT "${_note}" STREQUAL "migrated")
|
||||
message(FATAL_ERROR "Expected PostgreSQL migration to set note column, got `${_note}`.")
|
||||
endif()
|
||||
|
||||
cppbessot_test_pgsql_drop_database("${_dev_connstr}")
|
||||
@@ -0,0 +1,52 @@
|
||||
include("${CMAKE_CURRENT_LIST_DIR}/../cmake/TestCommon.cmake")
|
||||
|
||||
cppbessot_test_require_real_pgsql_support()
|
||||
cppbessot_test_require_var(CPPBESSOT_DB_PGSQL_PRODDEV_CONNSTR)
|
||||
cppbessot_test_case_dir(_case_dir)
|
||||
cppbessot_test_pgsql_isolated_connstr(_prod_connstr "${CPPBESSOT_DB_PGSQL_PRODDEV_CONNSTR}" "prod")
|
||||
cppbessot_test_pgsql_isolated_connstr(_proddev_connstr "${CPPBESSOT_DB_PGSQL_PRODDEV_CONNSTR}" "proddev")
|
||||
|
||||
cppbessot_test_pgsql_reset_database("${_prod_connstr}")
|
||||
cppbessot_test_pgsql_drop_database("${_proddev_connstr}")
|
||||
cppbessot_test_pgsql_exec(
|
||||
"${_prod_connstr}"
|
||||
"CREATE TABLE sample(id TEXT PRIMARY KEY); INSERT INTO sample(id) VALUES ('prod-row');")
|
||||
|
||||
cppbessot_test_pgsql_clone_command(
|
||||
_clone_command
|
||||
"${_prod_connstr}"
|
||||
"${_proddev_connstr}")
|
||||
cppbessot_test_cache_string_setting(_prod_setting "CPPBESSOT_DB_PGSQL_PROD_CONNSTR" "${_prod_connstr}")
|
||||
cppbessot_test_cache_string_setting(_proddev_setting "CPPBESSOT_DB_PGSQL_PRODDEV_CONNSTR" "${_proddev_connstr}")
|
||||
cppbessot_test_cache_string_setting(_clone_setting "CPPBESSOT_DB_PGSQL_CLONE_PROD_TO_PRODDEV_COMMAND" "${_clone_command}")
|
||||
cppbessot_test_write_project(
|
||||
"${_case_dir}"
|
||||
"set(DB_TARGET \"proddev\" CACHE STRING \"\")\n"
|
||||
"set(DB_MIGRATE_WITH \"v1.0-v1.1\" CACHE STRING \"\")\n"
|
||||
"${_prod_setting}"
|
||||
"${_proddev_setting}"
|
||||
"${_clone_setting}")
|
||||
cppbessot_test_add_schema("${_case_dir}" "v1.1")
|
||||
cppbessot_test_add_sql_file("${_case_dir}/db/migrations/v1.0-v1.1/postgre/01-migrate.sql"
|
||||
"ALTER TABLE sample ADD COLUMN note TEXT;\n"
|
||||
"UPDATE sample SET note = 'cloned' WHERE id = 'prod-row';\n")
|
||||
|
||||
cppbessot_test_configure_project("${_case_dir}" "${_case_dir}/build" _cfg_result _cfg_stdout _cfg_stderr)
|
||||
cppbessot_test_assert_success("${_cfg_result}" "${_cfg_stderr}" "fixture configure")
|
||||
cppbessot_test_build_target("${_case_dir}/build" "db_migrate" _build_result _build_stdout _build_stderr)
|
||||
cppbessot_test_assert_success("${_build_result}" "${_build_stderr}" "db_migrate pgsql proddev clone real")
|
||||
|
||||
cppbessot_test_pgsql_query_scalar(_proddev_note "${_proddev_connstr}" "SELECT note FROM sample WHERE id='prod-row';")
|
||||
if(NOT "${_proddev_note}" STREQUAL "cloned")
|
||||
message(FATAL_ERROR "Expected migrated proddev clone to contain note column, got `${_proddev_note}`.")
|
||||
endif()
|
||||
cppbessot_test_pgsql_query_scalar(
|
||||
_prod_note_column_count
|
||||
"${_prod_connstr}"
|
||||
"SELECT COUNT(*) FROM information_schema.columns WHERE table_schema = 'public' AND table_name = 'sample' AND column_name = 'note';")
|
||||
if(NOT "${_prod_note_column_count}" STREQUAL "0")
|
||||
message(FATAL_ERROR "Expected prod database to remain unchanged after proddev migration.")
|
||||
endif()
|
||||
|
||||
cppbessot_test_pgsql_drop_database("${_proddev_connstr}")
|
||||
cppbessot_test_pgsql_drop_database("${_prod_connstr}")
|
||||
@@ -0,0 +1,35 @@
|
||||
include("${CMAKE_CURRENT_LIST_DIR}/../cmake/TestCommon.cmake")
|
||||
|
||||
cppbessot_test_require_real_pgsql_support()
|
||||
cppbessot_test_require_var(CPPBESSOT_DB_PGSQL_PRODDEV_CONNSTR)
|
||||
cppbessot_test_case_dir(_case_dir)
|
||||
cppbessot_test_pgsql_isolated_connstr(_proddev_connstr "${CPPBESSOT_DB_PGSQL_PRODDEV_CONNSTR}" "proddev")
|
||||
|
||||
cppbessot_test_pgsql_reset_database("${_proddev_connstr}")
|
||||
cppbessot_test_pgsql_exec(
|
||||
"${_proddev_connstr}"
|
||||
"CREATE TABLE sample(id TEXT PRIMARY KEY); INSERT INTO sample(id) VALUES ('stale-row');")
|
||||
|
||||
cppbessot_test_cache_string_setting(_proddev_setting "CPPBESSOT_DB_PGSQL_PRODDEV_CONNSTR" "${_proddev_connstr}")
|
||||
cppbessot_test_write_project(
|
||||
"${_case_dir}"
|
||||
"set(DB_TARGET \"proddev\" CACHE STRING \"\")\n"
|
||||
"set(DB_MIGRATE_WITH \"v1.0-v1.1\" CACHE STRING \"\")\n"
|
||||
"set(DB_MIGRATE_PRODDEV_USE_STALE ON CACHE BOOL \"\")\n"
|
||||
"${_proddev_setting}")
|
||||
cppbessot_test_add_schema("${_case_dir}" "v1.1")
|
||||
cppbessot_test_add_sql_file("${_case_dir}/db/migrations/v1.0-v1.1/postgre/01-migrate.sql"
|
||||
"ALTER TABLE sample ADD COLUMN note TEXT;\n"
|
||||
"UPDATE sample SET note = 'stale' WHERE id = 'stale-row';\n")
|
||||
|
||||
cppbessot_test_configure_project("${_case_dir}" "${_case_dir}/build" _cfg_result _cfg_stdout _cfg_stderr)
|
||||
cppbessot_test_assert_success("${_cfg_result}" "${_cfg_stderr}" "fixture configure")
|
||||
cppbessot_test_build_target("${_case_dir}/build" "db_migrate" _build_result _build_stdout _build_stderr)
|
||||
cppbessot_test_assert_success("${_build_result}" "${_build_stderr}" "db_migrate pgsql proddev stale real")
|
||||
|
||||
cppbessot_test_pgsql_query_scalar(_note "${_proddev_connstr}" "SELECT note FROM sample WHERE id='stale-row';")
|
||||
if(NOT "${_note}" STREQUAL "stale")
|
||||
message(FATAL_ERROR "Expected PostgreSQL stale proddev migration to update note column, got `${_note}`.")
|
||||
endif()
|
||||
|
||||
cppbessot_test_pgsql_drop_database("${_proddev_connstr}")
|
||||
@@ -0,0 +1,27 @@
|
||||
include("${CMAKE_CURRENT_LIST_DIR}/../cmake/TestCommon.cmake")
|
||||
|
||||
cppbessot_test_case_dir(_case_dir)
|
||||
set(_tool_dir "${_case_dir}/tools")
|
||||
set(_log_file "${_case_dir}/psql.log")
|
||||
set(CPPBESSOT_TEST_LOG "${_log_file}")
|
||||
cppbessot_test_write_file("${_log_file}" "")
|
||||
cppbessot_test_write_mock_psql("${_tool_dir}/psql")
|
||||
cppbessot_test_set_path_with_tool_dir("${_tool_dir}")
|
||||
set(ENV{CPPBESSOT_TEST_LOG} "${_log_file}")
|
||||
set(ENV{CPPBESSOT_TEST_PSQL_FAIL_SELECT} "1")
|
||||
set(ENV{CPPBESSOT_TEST_PSQL_FAIL_ALL} "0")
|
||||
|
||||
cppbessot_test_write_project(
|
||||
"${_case_dir}"
|
||||
"set(DB_TARGET \"proddev\" CACHE STRING \"\")\n"
|
||||
"set(DB_MIGRATE_WITH \"v1.0-v1.1\" CACHE STRING \"\")\n"
|
||||
"set(DB_MIGRATE_PRODDEV_USE_STALE ON CACHE BOOL \"\")\n"
|
||||
"set(CPPBESSOT_DB_PGSQL_PRODDEV_CONNSTR \"dbname=proddev_db\" CACHE STRING \"\")\n")
|
||||
cppbessot_test_add_schema("${_case_dir}" "v1.1")
|
||||
cppbessot_test_add_sql_file("${_case_dir}/db/migrations/v1.0-v1.1/postgre/01-migrate.sql"
|
||||
"ALTER TABLE sample ADD COLUMN note TEXT;\n")
|
||||
|
||||
cppbessot_test_configure_project("${_case_dir}" "${_case_dir}/build" _cfg_result _cfg_stdout _cfg_stderr)
|
||||
cppbessot_test_assert_success("${_cfg_result}" "${_cfg_stderr}" "fixture configure")
|
||||
cppbessot_test_build_target("${_case_dir}/build" "db_migrate" _build_result _build_stdout _build_stderr)
|
||||
cppbessot_test_assert_failure_contains("${_build_result}" "${_build_stderr}" "no current stale proddev target")
|
||||
@@ -0,0 +1,32 @@
|
||||
include("${CMAKE_CURRENT_LIST_DIR}/../cmake/TestCommon.cmake")
|
||||
|
||||
cppbessot_test_case_dir(_case_dir)
|
||||
set(_tool_dir "${_case_dir}/tools")
|
||||
set(_log_file "${_case_dir}/psql.log")
|
||||
set(CPPBESSOT_TEST_LOG "${_log_file}")
|
||||
cppbessot_test_write_file("${_log_file}" "")
|
||||
cppbessot_test_write_mock_psql("${_tool_dir}/psql")
|
||||
cppbessot_test_set_path_with_tool_dir("${_tool_dir}")
|
||||
set(ENV{CPPBESSOT_TEST_LOG} "${_log_file}")
|
||||
set(ENV{CPPBESSOT_TEST_PSQL_FAIL_ALL} "0")
|
||||
set(ENV{CPPBESSOT_TEST_PSQL_FAIL_SELECT} "0")
|
||||
|
||||
cppbessot_test_write_project(
|
||||
"${_case_dir}"
|
||||
"set(DB_TARGET \"tests\" CACHE STRING \"\")\n"
|
||||
"set(CPPBESSOT_DB_PGSQL_TESTS_CONNSTR \"dbname=tests_db\" CACHE STRING \"\")\n")
|
||||
cppbessot_test_add_schema("${_case_dir}" "v1.1")
|
||||
cppbessot_test_add_sql_file("${_case_dir}/db/v1.1/generated-sql-ddl/postgre/01-schema.sql"
|
||||
"CREATE TABLE sample(id TEXT PRIMARY KEY);\n")
|
||||
cppbessot_test_add_sql_file("${_case_dir}/db/v1.1/generated-sql-ddl/postgre/02-seed.sql"
|
||||
"INSERT INTO sample(id) VALUES ('row-1');\n")
|
||||
|
||||
cppbessot_test_configure_project("${_case_dir}" "${_case_dir}/build" _cfg_result _cfg_stdout _cfg_stderr)
|
||||
cppbessot_test_assert_success("${_cfg_result}" "${_cfg_stderr}" "fixture configure")
|
||||
cppbessot_test_build_target("${_case_dir}/build" "db_createfrom" _build_result _build_stdout _build_stderr)
|
||||
cppbessot_test_assert_success("${_build_result}" "${_build_stderr}" "db_createfrom pgsql tests mock")
|
||||
|
||||
file(READ "${_log_file}" _log_contents)
|
||||
cppbessot_test_assert_contains("${_log_contents}" "sqlcmd:DROP SCHEMA IF EXISTS public CASCADE; CREATE SCHEMA public;" "pgsql reset log")
|
||||
cppbessot_test_assert_contains("${_log_contents}" "sqlfile:01-schema.sql" "pgsql schema log")
|
||||
cppbessot_test_assert_contains("${_log_contents}" "sqlfile:02-seed.sql" "pgsql seed log")
|
||||
@@ -0,0 +1,15 @@
|
||||
include("${CMAKE_CURRENT_LIST_DIR}/../cmake/TestCommon.cmake")
|
||||
|
||||
cppbessot_test_case_dir(_case_dir)
|
||||
cppbessot_test_write_project(
|
||||
"${_case_dir}"
|
||||
"set(DB_TARGET \"proddev\" CACHE STRING \"\")\n"
|
||||
"set(CPPBESSOT_DB_SQLITE_PRODDEV_PATH \"${_case_dir}/live/proddev.sqlite\" CACHE STRING \"\")\n")
|
||||
cppbessot_test_add_schema("${_case_dir}" "v1.1")
|
||||
cppbessot_test_add_sql_file("${_case_dir}/db/v1.1/generated-sql-ddl/sqlite/01-schema.sql"
|
||||
"CREATE TABLE sample(id TEXT);\n")
|
||||
|
||||
cppbessot_test_configure_project("${_case_dir}" "${_case_dir}/build" _cfg_result _cfg_stdout _cfg_stderr)
|
||||
cppbessot_test_assert_success("${_cfg_result}" "${_cfg_stderr}" "fixture configure")
|
||||
cppbessot_test_build_target("${_case_dir}/build" "db_createfrom" _build_result _build_stdout _build_stderr)
|
||||
cppbessot_test_assert_failure_contains("${_build_result}" "${_build_stderr}" "does not support DB_TARGET=proddev")
|
||||
@@ -0,0 +1,68 @@
|
||||
include("${CMAKE_CURRENT_LIST_DIR}/../cmake/TestCommon.cmake")
|
||||
|
||||
cppbessot_test_require_var(CPPBESSOT_TEST_BINARY_DIR)
|
||||
cppbessot_test_require_var(CPPBESSOT_TEST_MODULE_SOURCE_DIR)
|
||||
set(_build_dir "${CPPBESSOT_TEST_BINARY_DIR}/regression-build")
|
||||
cppbessot_test_reset_dir("${_build_dir}")
|
||||
get_filename_component(_repo_root "${CPPBESSOT_TEST_MODULE_SOURCE_DIR}/../.." ABSOLUTE)
|
||||
|
||||
execute_process(
|
||||
COMMAND "${CMAKE_COMMAND}"
|
||||
-S "${_repo_root}"
|
||||
-B "${_build_dir}"
|
||||
-DDB_SCHEMA_DIR_TO_GENERATE=v1.1
|
||||
-DDB_SCHEMA_DIR_MIGRATION_FROM=v1.1
|
||||
-DDB_SCHEMA_DIR_MIGRATION_TO=v1.2
|
||||
-DCPPBESSOT_AUTO_ENABLE=ON
|
||||
-DCOURESILIENT_USE_LOCAL_CPPBESSOT_ENV=OFF
|
||||
-DCPPBESSOT_DB_PGSQL_PROD_CONNSTR=
|
||||
-DCPPBESSOT_DB_PGSQL_DEV_CONNSTR=
|
||||
-DCPPBESSOT_DB_PGSQL_PRODDEV_CONNSTR=
|
||||
-DCPPBESSOT_DB_PGSQL_TESTS_CONNSTR=
|
||||
-DCPPBESSOT_DB_SQLITE_PROD_PATH=
|
||||
-DCPPBESSOT_DB_SQLITE_DEV_PATH=
|
||||
-DCPPBESSOT_DB_SQLITE_PRODDEV_PATH=
|
||||
-DCPPBESSOT_DB_SQLITE_TESTS_PATH=
|
||||
RESULT_VARIABLE _cfg_result
|
||||
OUTPUT_VARIABLE _cfg_stdout
|
||||
ERROR_VARIABLE _cfg_stderr
|
||||
)
|
||||
cppbessot_test_assert_success("${_cfg_result}" "${_cfg_stderr}" "fixture configure")
|
||||
cppbessot_test_build_target_dry_run("${_build_dir}" "cppBeSsotOpenAiModelGen" _model_result _model_stdout _model_stderr)
|
||||
cppbessot_test_assert_success("${_model_result}" "${_model_stderr}" "dry-run openai model lib build")
|
||||
cppbessot_test_build_target_dry_run("${_build_dir}" "cppBeSsotOdbSqlite" _odb_sqlite_result _odb_sqlite_stdout _odb_sqlite_stderr)
|
||||
cppbessot_test_assert_success("${_odb_sqlite_result}" "${_odb_sqlite_stderr}" "dry-run sqlite ODB lib build")
|
||||
cppbessot_test_build_target_dry_run("${_build_dir}" "cppBeSsotOdbPgSql" _odb_pgsql_result _odb_pgsql_stdout _odb_pgsql_stderr)
|
||||
cppbessot_test_assert_success("${_odb_pgsql_result}" "${_odb_pgsql_stderr}" "dry-run postgre ODB lib build")
|
||||
cppbessot_test_build_target_dry_run("${_build_dir}" "db_gen_migrations" _mig_result _mig_stdout _mig_stderr)
|
||||
cppbessot_test_assert_success("${_mig_result}" "${_mig_stderr}" "dry-run migration generation build")
|
||||
|
||||
# v1.3 includes enum-only schemas that must not become ODB library sources.
|
||||
set(_v13_build_dir "${CPPBESSOT_TEST_BINARY_DIR}/regression-build-v1.3")
|
||||
cppbessot_test_reset_dir("${_v13_build_dir}")
|
||||
execute_process(
|
||||
COMMAND "${CMAKE_COMMAND}"
|
||||
-S "${_repo_root}"
|
||||
-B "${_v13_build_dir}"
|
||||
-DDB_SCHEMA_DIR_TO_GENERATE=v1.3
|
||||
-DDB_SCHEMA_DIR_MIGRATION_FROM=v1.2
|
||||
-DDB_SCHEMA_DIR_MIGRATION_TO=v1.3
|
||||
-DCPPBESSOT_AUTO_ENABLE=ON
|
||||
-DCOURESILIENT_USE_LOCAL_CPPBESSOT_ENV=OFF
|
||||
-DCPPBESSOT_DB_PGSQL_PROD_CONNSTR=
|
||||
-DCPPBESSOT_DB_PGSQL_DEV_CONNSTR=
|
||||
-DCPPBESSOT_DB_PGSQL_PRODDEV_CONNSTR=
|
||||
-DCPPBESSOT_DB_PGSQL_TESTS_CONNSTR=
|
||||
-DCPPBESSOT_DB_SQLITE_PROD_PATH=
|
||||
-DCPPBESSOT_DB_SQLITE_DEV_PATH=
|
||||
-DCPPBESSOT_DB_SQLITE_PRODDEV_PATH=
|
||||
-DCPPBESSOT_DB_SQLITE_TESTS_PATH=
|
||||
RESULT_VARIABLE _v13_cfg_result
|
||||
OUTPUT_VARIABLE _v13_cfg_stdout
|
||||
ERROR_VARIABLE _v13_cfg_stderr
|
||||
)
|
||||
cppbessot_test_assert_success("${_v13_cfg_result}" "${_v13_cfg_stderr}" "v1.3 fixture configure")
|
||||
cppbessot_test_build_target_dry_run("${_v13_build_dir}" "cppBeSsotOdbSqlite" _v13_sqlite_result _v13_sqlite_stdout _v13_sqlite_stderr)
|
||||
cppbessot_test_assert_success("${_v13_sqlite_result}" "${_v13_sqlite_stderr}" "v1.3 dry-run sqlite ODB lib build")
|
||||
cppbessot_test_build_target_dry_run("${_v13_build_dir}" "cppBeSsotOdbPgSql" _v13_pgsql_result _v13_pgsql_stdout _v13_pgsql_stderr)
|
||||
cppbessot_test_assert_success("${_v13_pgsql_result}" "${_v13_pgsql_stderr}" "v1.3 dry-run postgre ODB lib build")
|
||||
@@ -0,0 +1,29 @@
|
||||
include("${CMAKE_CURRENT_LIST_DIR}/../cmake/TestCommon.cmake")
|
||||
|
||||
cppbessot_test_case_dir(_case_dir)
|
||||
set(_db_path "${_case_dir}/live/dev.sqlite")
|
||||
cppbessot_test_write_project(
|
||||
"${_case_dir}"
|
||||
"set(CPPBESSOT_DB_SQLITE_DEV_PATH \"${_db_path}\" CACHE STRING \"\")\n")
|
||||
cppbessot_test_add_schema("${_case_dir}" "v1.1")
|
||||
cppbessot_test_add_sql_file("${_case_dir}/db/v1.1/generated-sql-ddl/sqlite/01-schema.sql"
|
||||
"CREATE TABLE sample(id TEXT PRIMARY KEY, note TEXT);\n")
|
||||
cppbessot_test_add_sql_file("${_case_dir}/db/v1.1/generated-sql-ddl/sqlite/02-seed.sql"
|
||||
"INSERT INTO sample(id, note) VALUES ('seed-1', 'created-second');\n")
|
||||
cppbessot_test_sqlite_exec("${_db_path}"
|
||||
"CREATE TABLE old_data(id TEXT); INSERT INTO old_data(id) VALUES ('legacy');")
|
||||
|
||||
cppbessot_test_configure_project("${_case_dir}" "${_case_dir}/build" _cfg_result _cfg_stdout _cfg_stderr)
|
||||
cppbessot_test_assert_success("${_cfg_result}" "${_cfg_stderr}" "fixture configure")
|
||||
cppbessot_test_build_target("${_case_dir}/build" "db_createfrom" _build_result _build_stdout _build_stderr)
|
||||
cppbessot_test_assert_success("${_build_result}" "${_build_stderr}" "db_createfrom sqlite")
|
||||
|
||||
cppbessot_test_sqlite_query_scalar(_row_count "${_db_path}" "SELECT COUNT(*) FROM sample;")
|
||||
if(NOT "${_row_count}" STREQUAL "1")
|
||||
message(FATAL_ERROR "Expected seeded sample row after recreate, got `${_row_count}`.")
|
||||
endif()
|
||||
cppbessot_test_sqlite_query_scalar(_old_table_count "${_db_path}"
|
||||
"SELECT COUNT(*) FROM sqlite_master WHERE type='table' AND name='old_data';")
|
||||
if(NOT "${_old_table_count}" STREQUAL "0")
|
||||
message(FATAL_ERROR "Expected old_data table to be removed during recreate.")
|
||||
endif()
|
||||
@@ -0,0 +1,14 @@
|
||||
include("${CMAKE_CURRENT_LIST_DIR}/../cmake/TestCommon.cmake")
|
||||
|
||||
cppbessot_test_case_dir(_case_dir)
|
||||
cppbessot_test_write_project(
|
||||
"${_case_dir}"
|
||||
"set(CPPBESSOT_DB_SQLITE_DEV_PATH \"${_case_dir}/live/dev.sqlite\" CACHE STRING \"\")\n")
|
||||
cppbessot_test_add_schema("${_case_dir}" "v1.1")
|
||||
cppbessot_test_add_sql_file("${_case_dir}/db/v1.1/generated-sql-ddl/sqlite/01-schema.sql"
|
||||
"CREATE TABLE sample(id TEXT PRIMARY KEY);\nBROKEN SQL;\n")
|
||||
|
||||
cppbessot_test_configure_project("${_case_dir}" "${_case_dir}/build" _cfg_result _cfg_stdout _cfg_stderr)
|
||||
cppbessot_test_assert_success("${_cfg_result}" "${_cfg_stderr}" "fixture configure")
|
||||
cppbessot_test_build_target("${_case_dir}/build" "db_createfrom" _build_result _build_stdout _build_stderr)
|
||||
cppbessot_test_assert_failure_contains("${_build_result}" "${_build_stderr}" "SQLite SQL apply failed")
|
||||
@@ -0,0 +1,23 @@
|
||||
include("${CMAKE_CURRENT_LIST_DIR}/../cmake/TestCommon.cmake")
|
||||
|
||||
cppbessot_test_case_dir(_case_dir)
|
||||
set(_db_path "${_case_dir}/live/dev.sqlite")
|
||||
cppbessot_test_write_project(
|
||||
"${_case_dir}"
|
||||
"set(DB_MIGRATE_WITH \"v1.0-v1.1\" CACHE STRING \"\")\n"
|
||||
"set(CPPBESSOT_DB_SQLITE_DEV_PATH \"${_db_path}\" CACHE STRING \"\")\n")
|
||||
cppbessot_test_add_schema("${_case_dir}" "v1.1")
|
||||
cppbessot_test_write_file("${_case_dir}/db/migrations/v1.0-v1.1/README.txt" "fixture\n")
|
||||
cppbessot_test_add_sql_file("${_case_dir}/db/migrations/v1.0-v1.1/sqlite/01-migrate.sql"
|
||||
"ALTER TABLE sample ADD COLUMN note TEXT DEFAULT 'migrated';\n")
|
||||
cppbessot_test_sqlite_exec("${_db_path}"
|
||||
"CREATE TABLE sample(id TEXT PRIMARY KEY); INSERT INTO sample(id) VALUES ('row-1');")
|
||||
|
||||
cppbessot_test_configure_project("${_case_dir}" "${_case_dir}/build" _cfg_result _cfg_stdout _cfg_stderr)
|
||||
cppbessot_test_assert_success("${_cfg_result}" "${_cfg_stderr}" "fixture configure")
|
||||
cppbessot_test_build_target("${_case_dir}/build" "db_migrate" _build_result _build_stdout _build_stderr)
|
||||
cppbessot_test_assert_success("${_build_result}" "${_build_stderr}" "db_migrate sqlite")
|
||||
cppbessot_test_sqlite_query_scalar(_note "${_db_path}" "SELECT note FROM sample WHERE id='row-1';")
|
||||
if(NOT "${_note}" STREQUAL "migrated")
|
||||
message(FATAL_ERROR "Expected migration to add note column with default value, got `${_note}`.")
|
||||
endif()
|
||||
@@ -0,0 +1,28 @@
|
||||
include("${CMAKE_CURRENT_LIST_DIR}/../cmake/TestCommon.cmake")
|
||||
|
||||
cppbessot_test_case_dir(_case_dir)
|
||||
set(_prod_db "${_case_dir}/live/prod.sqlite")
|
||||
set(_proddev_db "${_case_dir}/live/proddev.sqlite")
|
||||
set(_log_file "${_case_dir}/clone.log")
|
||||
cppbessot_test_sqlite_exec("${_prod_db}"
|
||||
"CREATE TABLE sample(id TEXT PRIMARY KEY); INSERT INTO sample(id) VALUES ('prod-row');")
|
||||
cppbessot_test_write_project(
|
||||
"${_case_dir}"
|
||||
"set(DB_TARGET \"proddev\" CACHE STRING \"\")\n"
|
||||
"set(DB_MIGRATE_WITH \"v1.0-v1.1\" CACHE STRING \"\")\n"
|
||||
"set(CPPBESSOT_DB_SQLITE_PROD_PATH \"${_prod_db}\" CACHE STRING \"\")\n"
|
||||
"set(CPPBESSOT_DB_SQLITE_PRODDEV_PATH \"${_proddev_db}\" CACHE STRING \"\")\n"
|
||||
"set(CPPBESSOT_DB_SQLITE_CLONE_PROD_TO_PRODDEV_COMMAND \"cp '${_prod_db}' '${_proddev_db}' && printf 'clone\\\\n' >> '${_log_file}'\" CACHE STRING \"\")\n")
|
||||
cppbessot_test_add_schema("${_case_dir}" "v1.1")
|
||||
cppbessot_test_add_sql_file("${_case_dir}/db/migrations/v1.0-v1.1/sqlite/01-migrate.sql"
|
||||
"ALTER TABLE sample ADD COLUMN note TEXT DEFAULT 'cloned';\n")
|
||||
|
||||
cppbessot_test_configure_project("${_case_dir}" "${_case_dir}/build" _cfg_result _cfg_stdout _cfg_stderr)
|
||||
cppbessot_test_assert_success("${_cfg_result}" "${_cfg_stderr}" "fixture configure")
|
||||
cppbessot_test_build_target("${_case_dir}/build" "db_migrate" _build_result _build_stdout _build_stderr)
|
||||
cppbessot_test_assert_success("${_build_result}" "${_build_stderr}" "db_migrate sqlite proddev clone")
|
||||
cppbessot_test_assert_file_exists("${_proddev_db}")
|
||||
cppbessot_test_sqlite_query_scalar(_note "${_proddev_db}" "SELECT note FROM sample WHERE id='prod-row';")
|
||||
if(NOT "${_note}" STREQUAL "cloned")
|
||||
message(FATAL_ERROR "Expected migrated proddev clone to contain note column, got `${_note}`.")
|
||||
endif()
|
||||
@@ -0,0 +1,18 @@
|
||||
include("${CMAKE_CURRENT_LIST_DIR}/../cmake/TestCommon.cmake")
|
||||
|
||||
cppbessot_test_case_dir(_case_dir)
|
||||
set(_proddev_db "${_case_dir}/live/proddev.sqlite")
|
||||
cppbessot_test_write_project(
|
||||
"${_case_dir}"
|
||||
"set(DB_TARGET \"proddev\" CACHE STRING \"\")\n"
|
||||
"set(DB_MIGRATE_WITH \"v1.0-v1.1\" CACHE STRING \"\")\n"
|
||||
"set(DB_MIGRATE_PRODDEV_USE_STALE ON CACHE BOOL \"\")\n"
|
||||
"set(CPPBESSOT_DB_SQLITE_PRODDEV_PATH \"${_proddev_db}\" CACHE STRING \"\")\n")
|
||||
cppbessot_test_add_schema("${_case_dir}" "v1.1")
|
||||
cppbessot_test_add_sql_file("${_case_dir}/db/migrations/v1.0-v1.1/sqlite/01-migrate.sql"
|
||||
"ALTER TABLE sample ADD COLUMN note TEXT DEFAULT 'ok';\n")
|
||||
|
||||
cppbessot_test_configure_project("${_case_dir}" "${_case_dir}/build" _cfg_result _cfg_stdout _cfg_stderr)
|
||||
cppbessot_test_assert_success("${_cfg_result}" "${_cfg_stderr}" "fixture configure")
|
||||
cppbessot_test_build_target("${_case_dir}/build" "db_migrate" _build_result _build_stdout _build_stderr)
|
||||
cppbessot_test_assert_failure_contains("${_build_result}" "${_build_stderr}" "no current stale proddev target")
|
||||
@@ -0,0 +1,30 @@
|
||||
include("${CMAKE_CURRENT_LIST_DIR}/../cmake/TestCommon.cmake")
|
||||
|
||||
cppbessot_test_case_dir(_case_dir)
|
||||
set(_db_path "${_case_dir}/live/tests.sqlite")
|
||||
cppbessot_test_write_project(
|
||||
"${_case_dir}"
|
||||
"set(DB_TARGET \"tests\" CACHE STRING \"\")\n"
|
||||
"set(CPPBESSOT_DB_SQLITE_TESTS_PATH \"${_db_path}\" CACHE STRING \"\")\n")
|
||||
cppbessot_test_add_schema("${_case_dir}" "v1.1")
|
||||
cppbessot_test_add_sql_file("${_case_dir}/db/v1.1/generated-sql-ddl/sqlite/01-schema.sql"
|
||||
"CREATE TABLE sample(id TEXT PRIMARY KEY, note TEXT);\n")
|
||||
cppbessot_test_add_sql_file("${_case_dir}/db/v1.1/generated-sql-ddl/sqlite/02-seed.sql"
|
||||
"INSERT INTO sample(id, note) VALUES ('seed-1', 'created-second');\n")
|
||||
cppbessot_test_sqlite_exec("${_db_path}"
|
||||
"CREATE TABLE old_data(id TEXT); INSERT INTO old_data(id) VALUES ('legacy');")
|
||||
|
||||
cppbessot_test_configure_project("${_case_dir}" "${_case_dir}/build" _cfg_result _cfg_stdout _cfg_stderr)
|
||||
cppbessot_test_assert_success("${_cfg_result}" "${_cfg_stderr}" "fixture configure")
|
||||
cppbessot_test_build_target("${_case_dir}/build" "db_createfrom" _build_result _build_stdout _build_stderr)
|
||||
cppbessot_test_assert_success("${_build_result}" "${_build_stderr}" "db_createfrom sqlite tests")
|
||||
|
||||
cppbessot_test_sqlite_query_scalar(_row_count "${_db_path}" "SELECT COUNT(*) FROM sample;")
|
||||
if(NOT "${_row_count}" STREQUAL "1")
|
||||
message(FATAL_ERROR "Expected seeded sample row after recreate, got `${_row_count}`.")
|
||||
endif()
|
||||
cppbessot_test_sqlite_query_scalar(_old_table_count "${_db_path}"
|
||||
"SELECT COUNT(*) FROM sqlite_master WHERE type='table' AND name='old_data';")
|
||||
if(NOT "${_old_table_count}" STREQUAL "0")
|
||||
message(FATAL_ERROR "Expected old_data table to be removed during recreate.")
|
||||
endif()
|
||||
@@ -11,6 +11,10 @@ function(cppbessot_add_odb_orm_test backend target_suffix connstr_var source_fil
|
||||
endif()
|
||||
|
||||
set(_target_name "cpp_odb_orm_${target_suffix}_${CPP_ODB_TEST_NAME_SUFFIX}")
|
||||
set(_sql_backend_dir "${backend}")
|
||||
if("${backend}" STREQUAL "pgsql")
|
||||
set(_sql_backend_dir "postgre")
|
||||
endif()
|
||||
add_executable(${_target_name} "${source_file}")
|
||||
add_dependencies(${_target_name} db_gen_sql_ddl)
|
||||
target_compile_features(${_target_name} PRIVATE cxx_std_20)
|
||||
@@ -22,12 +26,12 @@ function(cppbessot_add_odb_orm_test backend target_suffix connstr_var source_fil
|
||||
GTest::gtest_main)
|
||||
target_compile_definitions(${_target_name}
|
||||
PRIVATE
|
||||
CPPBESSOT_ODB_TEST_SQL_DIR="${PROJECT_SOURCE_DIR}/db/${DB_SCHEMA_DIR_TO_GENERATE}/generated-sql-ddl/${backend}"
|
||||
CPPBESSOT_ODB_TEST_SQL_DIR="${PROJECT_SOURCE_DIR}/db/${DB_SCHEMA_DIR_TO_GENERATE}/generated-sql-ddl/${_sql_backend_dir}"
|
||||
${connstr_var}_DEFAULT="${${connstr_var}}")
|
||||
|
||||
if("${backend}" STREQUAL "sqlite")
|
||||
target_link_libraries(${_target_name} PRIVATE "${CPPBESSOT_SQLITE_CLIENT_LIB}")
|
||||
elseif("${backend}" STREQUAL "postgre")
|
||||
elseif("${backend}" STREQUAL "pgsql")
|
||||
target_include_directories(${_target_name} PRIVATE "${CPPBESSOT_PGSQL_INCLUDE_DIR}")
|
||||
target_link_libraries(${_target_name} PRIVATE "${CPPBESSOT_PGSQL_CLIENT_LIB}")
|
||||
endif()
|
||||
@@ -40,13 +44,13 @@ endfunction()
|
||||
cppbessot_add_odb_orm_test(
|
||||
"sqlite"
|
||||
"sqlite"
|
||||
CPPBESSOT_ODB_TEST_SQLITE_CONNSTR
|
||||
CPPBESSOT_DB_SQLITE_PRODDEV_PATH
|
||||
"${CMAKE_CURRENT_SOURCE_DIR}/sqlite_orm_test.cpp"
|
||||
cppbessot::odb_sqlite)
|
||||
|
||||
cppbessot_add_odb_orm_test(
|
||||
"pgsql"
|
||||
"pgsql"
|
||||
CPPBESSOT_ODB_TEST_PGSQL_CONNSTR
|
||||
CPPBESSOT_DB_PGSQL_PRODDEV_CONNSTR
|
||||
"${CMAKE_CURRENT_SOURCE_DIR}/pgsql_orm_test.cpp"
|
||||
cppbessot::odb_pgsql)
|
||||
|
||||
@@ -54,8 +54,8 @@ void apply_pgsql_ddl(const std::string& connstr)
|
||||
TEST(PgsqlOdbOrm, PersistsLoadsQueriesAndErases)
|
||||
{
|
||||
const std::string connstr = cppbessot_env_or_default(
|
||||
"CPPBESSOT_ODB_TEST_PGSQL_CONNSTR",
|
||||
CPPBESSOT_ODB_TEST_PGSQL_CONNSTR_DEFAULT);
|
||||
"CPPBESSOT_DB_PGSQL_PRODDEV_CONNSTR",
|
||||
CPPBESSOT_DB_PGSQL_PRODDEV_CONNSTR_DEFAULT);
|
||||
apply_pgsql_ddl(connstr);
|
||||
odb::pgsql::database db(connstr);
|
||||
cppbessot_run_agent_orm_roundtrip<odb::pgsql::database, odb::pgsql::transaction>(db);
|
||||
|
||||
@@ -43,8 +43,8 @@ void apply_sqlite_ddl(const std::string& connstr)
|
||||
TEST(SqliteOdbOrm, PersistsLoadsQueriesAndErases)
|
||||
{
|
||||
const std::string connstr = cppbessot_env_or_default(
|
||||
"CPPBESSOT_ODB_TEST_SQLITE_CONNSTR",
|
||||
CPPBESSOT_ODB_TEST_SQLITE_CONNSTR_DEFAULT);
|
||||
"CPPBESSOT_DB_SQLITE_PRODDEV_PATH",
|
||||
CPPBESSOT_DB_SQLITE_PRODDEV_PATH_DEFAULT);
|
||||
std::filesystem::remove(connstr);
|
||||
apply_sqlite_ddl(connstr);
|
||||
odb::sqlite::database db(connstr, SQLITE_OPEN_READWRITE | SQLITE_OPEN_CREATE);
|
||||
|
||||
Reference in New Issue
Block a user