Update readme

This commit is contained in:
2026-04-30 00:17:20 -04:00
parent e00601c721
commit 3d26d89742
+626 -82
View File
@@ -1,110 +1,654 @@
# 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`, or `proddev`
## 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 test-related cache variables:
- `CPPBESSOT_ODB_TEST_SQLITE_CONNSTR`
- optional SQLite connection string for ODB runtime tests
- `CPPBESSOT_ODB_TEST_PGSQL_CONNSTR`
- optional PostgreSQL conninfo string for ODB runtime tests
- `CPPBESSOT_DB_ACTION_TEST_PGSQL_ADMIN_CONNSTR`
- optional PostgreSQL admin conninfo string used only by the real db-action test harness
## 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`
- `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`
- `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`
These point at the SQLite DB file to act on for `prod`, `dev`, or `proddev`.
### PostgreSQL Live DB Mapping Variables
- `CPPBESSOT_DB_PGSQL_PROD_CONNSTR`
- `CPPBESSOT_DB_PGSQL_DEV_CONNSTR`
- `CPPBESSOT_DB_PGSQL_PRODDEV_CONNSTR`
These are `psql`-compatible PostgreSQL connection strings for `prod`, `dev`, or `proddev`.
### 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`
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
### `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 test DB connection strings:
```bash
cmake -S cmake/cppbessot -B build-cppbessot-tests \
-DBUILD_TESTING=ON \
-DDB_SCHEMA_DIR_TO_GENERATE=test-schema-v1.2 \
-DCPPBESSOT_ODB_TEST_SQLITE_CONNSTR=/tmp/cppbessot-odb.sqlite \
-DCPPBESSOT_ODB_TEST_PGSQL_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
- `CPPBESSOT_DB_ACTION_TEST_PGSQL_ADMIN_CONNSTR` is non-empty
- 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_ACTION_TEST_PGSQL_ADMIN_CONNSTR="host=127.0.0.1 port=5432 dbname=postgres user=postgres password=postgres" \
-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.