Compare commits

...

12 Commits

41 changed files with 3907 additions and 175 deletions
+1
View File
@@ -1,3 +1,4 @@
# Ignore any directory named `db` or `build` anywhere in this repository.
**/build/
/b-*/
node_modules/
+16 -6
View File
@@ -12,12 +12,22 @@ endif()
include("${CMAKE_CURRENT_SOURCE_DIR}/cmake/dbDependencyCheck.cmake")
cppbessot_check_dependencies()
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")
set(CPPBESSOT_DB_ACTION_TEST_PGSQL_ADMIN_CONNSTR "" CACHE STRING
"Optional PostgreSQL admin conninfo string for real db-action test database lifecycle operations")
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")
+31 -16
View File
@@ -11,7 +11,7 @@ The OpenAPI file under a schema directory is the single source of truth. From th
- 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`
- run live DB actions against `dev`, `prod`, `proddev`, or `tests`
## Repository Layout
@@ -79,14 +79,16 @@ When configuring the standalone `cppbessot` repo itself, `DB_SCHEMA_DIR_TO_GENER
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:
The standalone top-level file also exposes these DB target mapping 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
- `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
@@ -133,6 +135,13 @@ cppbessot_enable()
- 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
@@ -146,7 +155,7 @@ cppbessot_enable()
- `DB_TARGET`
- default: `dev`
- selected live DB target for `db_createfrom` and `db_migrate`
- allowed values: `prod`, `proddev`, `dev`
- allowed values: `prod`, `proddev`, `dev`, `tests`
- `DB_CREATEFROM_SCHEMA_DIR`
- default: `DB_SCHEMA_DIR_TO_GENERATE`
@@ -168,16 +177,18 @@ 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`, or `proddev`.
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`, or `proddev`.
These are `psql`-compatible PostgreSQL connection strings for `prod`, `dev`, `proddev`, or `tests`.
### Prod-To-Proddev Clone Hook Variables
@@ -299,6 +310,7 @@ Primary variables:
- `CPPBESSOT_WORKDIR`
- `DB_SCHEMA_DIR_MIGRATION_FROM`
- `DB_SCHEMA_DIR_MIGRATION_TO`
- `CPPBESSOT_GEN_MIGRATION_BACKENDS`
Output:
@@ -309,6 +321,11 @@ 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`
@@ -613,14 +630,14 @@ ctest --test-dir build-cppbessot-tests --output-on-failure
### ODB Runtime Tests
Provide test DB connection strings:
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_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"
-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
@@ -629,7 +646,6 @@ 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:
@@ -638,7 +654,6 @@ Example:
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"
+22 -1
View File
@@ -12,6 +12,7 @@ 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")
@@ -31,12 +32,25 @@ 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, or dev")
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)
@@ -59,17 +73,23 @@ 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()
@@ -245,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:
+3 -1
View File
@@ -37,9 +37,11 @@ function(_cppbessot_db_action_common_cache_args out_var)
"-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_PRODDEV_CONNSTR=${CPPBESSOT_DB_PGSQL_PRODDEV_CONNSTR}"
"-DCPPBESSOT_DB_PGSQL_TESTS_CONNSTR=${CPPBESSOT_DB_PGSQL_TESTS_CONNSTR}")
set(${out_var} "${_args}" PARENT_SCOPE)
endfunction()
+3 -2
View File
@@ -14,7 +14,8 @@ 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")
message(FATAL_ERROR "DB_TARGET must be one of: prod, proddev, dev.")
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()
+7 -8
View File
@@ -11,7 +11,6 @@ function(_cppbessot_publish_dependency_outputs)
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_PSQL_EXECUTABLE "${CPPBESSOT_PSQL_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)
@@ -57,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
@@ -91,9 +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 "${PROJECT_SOURCE_DIR}"
WORKING_DIRECTORY "${_module_root}"
RESULT_VARIABLE _help_result
OUTPUT_QUIET
ERROR_VARIABLE _help_stderr
@@ -106,7 +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 "${PROJECT_SOURCE_DIR}"
WORKING_DIRECTORY "${_module_root}"
RESULT_VARIABLE _version_result
OUTPUT_QUIET
ERROR_VARIABLE _version_stderr
@@ -155,12 +156,10 @@ function(cppbessot_check_dependencies)
CPPBESSOT_DB_PGSQL_PROD_CONNSTR
CPPBESSOT_DB_PGSQL_DEV_CONNSTR
CPPBESSOT_DB_PGSQL_PRODDEV_CONNSTR
CPPBESSOT_DB_ACTION_TEST_PGSQL_ADMIN_CONNSTR)
CPPBESSOT_DB_PGSQL_TESTS_CONNSTR)
if(_cppbessot_require_psql)
_cppbessot_require_program(CPPBESSOT_PSQL_EXECUTABLE psql
_cppbessot_require_program(_cppbessot_psql_executable psql
"Install the PostgreSQL client CLI so live PostgreSQL DB actions can run.")
else()
set(CPPBESSOT_PSQL_EXECUTABLE "")
endif()
_cppbessot_require_npm_package("${CPPBESSOT_NPM_EXECUTABLE}" "@openapitools/openapi-generator-cli")
+11 -7
View File
@@ -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()
+6 -4
View File
@@ -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
View File
@@ -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
View File
@@ -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()
+2
View File
@@ -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
)
+1 -1
View File
@@ -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
+180 -24
View File
@@ -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:
@@ -116,19 +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}")
cppbessot_assert_openapi_exists("${schema_dir}")
set(_openapi_file "${_schema_dir_path}/openapi/openapi.yaml")
# - <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)
@@ -136,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()
@@ -155,10 +180,52 @@ function(cppbessot_get_openapi_schema_names out_var schema_dir)
endforeach()
if(NOT _schema_names)
message(FATAL_ERROR
"No component schema names were found in ${_openapi_file}.")
if("${filter_mode}" STREQUAL "ODB_TABLE")
message(FATAL_ERROR
"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()
@@ -187,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`.
@@ -196,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()
+41
View File
@@ -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()
+6 -6
View File
@@ -25,7 +25,7 @@ function(cppbessot_db_action_target_exists out_var backend sqlite_path pgsql_con
endif()
endfunction()
function(cppbessot_db_action_get_clone_command out_var backend)
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()
@@ -34,11 +34,11 @@ function(cppbessot_db_action_get_clone_command out_var backend)
set(${out_var} "${_command}" PARENT_SCOPE)
endfunction()
function(cppbessot_db_action_invoke_clone_hook backend)
cppbessot_db_action_get_clone_command(_clone_command "${backend}")
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 proddev.")
"No clone command is configured for backend `${backend}` while preparing target `${target}`.")
endif()
execute_process(
@@ -50,7 +50,7 @@ function(cppbessot_db_action_invoke_clone_hook backend)
if(NOT _result EQUAL 0)
message(FATAL_ERROR
"Proddev clone command failed for backend `${backend}`.\n${_stdout}\n${_stderr}")
"Clone command failed for backend `${backend}` target `${target}`.\n${_stdout}\n${_stderr}")
endif()
endfunction()
@@ -68,7 +68,7 @@ function(cppbessot_db_action_prepare_proddev target backend use_stale sqlite_pat
return()
endif()
cppbessot_db_action_invoke_clone_hook("${backend}")
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
+49 -1
View File
@@ -104,8 +104,56 @@ function(cppbessot_db_action_collect_nonempty_sql_files out_var sql_dir)
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_nonempty_sql_files(_sql_files "${sql_dir}")
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()
@@ -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()
+1 -1
View File
@@ -26,7 +26,7 @@ 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_nonempty_sql_files(_sql_files "${_ddl_dir}")
cppbessot_db_action_collect_schema_sql_files(_sql_files "${_ddl_dir}")
if("${_backend}" STREQUAL "sqlite")
cppbessot_db_action_reset_sqlite_db("${_sqlite_path}")
+1 -1
View File
@@ -29,7 +29,7 @@ cppbessot_db_action_prepare_proddev(
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_nonempty_sql_files(_sql_files "${_sql_dir}")
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")
+3 -5
View File
@@ -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
+120 -15
View File
@@ -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(_raw_backends "sqlite;pgsql")
endif()
else()
set(_subdir postgre)
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}")
+3 -5
View File
@@ -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,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.
@@ -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}})
+2776
View File
File diff suppressed because it is too large Load Diff
+8
View File
@@ -0,0 +1,8 @@
{
"name": "cppbessot",
"private": true,
"devDependencies": {
"@openapitools/openapi-generator-cli": "^2.30.0",
"openapi-zod-client": "^1.18.3"
}
}
+17 -7
View File
@@ -3,10 +3,12 @@ function(_cppbessot_db_action_test_common_args out_var test_name script_name)
"-DCPPBESSOT_TEST_NAME=${test_name}"
"-DCPPBESSOT_TEST_BINARY_DIR=${CMAKE_CURRENT_BINARY_DIR}"
"-DCPPBESSOT_TEST_MODULE_SOURCE_DIR=${PROJECT_SOURCE_DIR}"
"-DCPPBESSOT_DB_ACTION_TEST_PGSQL_ADMIN_CONNSTR=${CPPBESSOT_DB_ACTION_TEST_PGSQL_ADMIN_CONNSTR}"
"-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()
@@ -24,9 +26,14 @@ function(cppbessot_add_real_pgsql_db_action_test test_name script_name)
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_ACTION_TEST_PGSQL_ADMIN_CONNSTR}" STREQUAL "")
message(STATUS "Skipping real PostgreSQL db-action test `${test_name}` because CPPBESSOT_DB_ACTION_TEST_PGSQL_ADMIN_CONNSTR is empty.")
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()
@@ -51,26 +58,29 @@ cppbessot_add_db_action_test(cppbessot_db_action_missing_migrate_with missing_mi
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_DEV_CONNSTR)
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_DEV_CONNSTR)
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_PROD_CONNSTR
CPPBESSOT_DB_PGSQL_PRODDEV_CONNSTR)
cppbessot_add_real_pgsql_db_action_test(
cppbessot_db_action_pgsql_proddev_stale_real
@@ -79,5 +89,5 @@ cppbessot_add_real_pgsql_db_action_test(
cppbessot_add_real_pgsql_db_action_test(
cppbessot_db_action_pgsql_backfill_real
pgsql_backfill_real.cmake
CPPBESSOT_DB_PGSQL_DEV_CONNSTR)
CPPBESSOT_DB_PGSQL_PRODDEV_CONNSTR)
cppbessot_add_db_action_test(cppbessot_db_action_regression regression_targets.cmake)
+65 -43
View File
@@ -84,7 +84,12 @@ function(cppbessot_test_add_schema root_dir schema_dir)
endfunction()
function(cppbessot_test_add_sql_file path content)
cppbessot_test_write_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)
@@ -162,8 +167,9 @@ endfunction()
function(cppbessot_test_has_real_pgsql_support out_var)
find_program(_psql psql)
if(_psql AND DEFINED CPPBESSOT_DB_ACTION_TEST_PGSQL_ADMIN_CONNSTR
AND NOT "${CPPBESSOT_DB_ACTION_TEST_PGSQL_ADMIN_CONNSTR}" STREQUAL "")
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)
@@ -174,10 +180,16 @@ 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` and CPPBESSOT_DB_ACTION_TEST_PGSQL_ADMIN_CONNSTR.")
"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(
@@ -228,6 +240,15 @@ function(cppbessot_test_pgsql_connstr_dbname out_var connstr)
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}")
@@ -270,23 +291,25 @@ 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(
"${CPPBESSOT_DB_ACTION_TEST_PGSQL_ADMIN_CONNSTR}"
"${_admin_connstr}"
"SELECT pg_terminate_backend(pid) FROM pg_stat_activity WHERE datname = ${_db_lit} AND pid <> pg_backend_pid();")
cppbessot_test_pgsql_exec(
"${CPPBESSOT_DB_ACTION_TEST_PGSQL_ADMIN_CONNSTR}"
"${_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(
"${CPPBESSOT_DB_ACTION_TEST_PGSQL_ADMIN_CONNSTR}"
"${_admin_connstr}"
"CREATE DATABASE ${_db_ident};")
endfunction()
@@ -295,46 +318,45 @@ function(cppbessot_test_pgsql_reset_database connstr)
cppbessot_test_pgsql_create_database("${connstr}")
endfunction()
function(cppbessot_test_pgsql_clone_database source_connstr target_connstr)
cppbessot_test_require_real_pgsql_support()
cppbessot_test_pgsql_connstr_dbname(_source_db "${source_connstr}")
cppbessot_test_pgsql_connstr_dbname(_target_db "${target_connstr}")
cppbessot_test_pgsql_escape_identifier(_source_ident "${_source_db}")
cppbessot_test_pgsql_escape_identifier(_target_ident "${_target_db}")
cppbessot_test_pgsql_drop_database("${target_connstr}")
cppbessot_test_pgsql_exec(
"${CPPBESSOT_DB_ACTION_TEST_PGSQL_ADMIN_CONNSTR}"
"CREATE DATABASE ${_target_ident} TEMPLATE ${_source_ident};")
endfunction()
function(cppbessot_test_shell_single_quote out_var text)
string(REPLACE "'" "'\"'\"'" _quoted "${text}")
set(${out_var} "'${_quoted}'" PARENT_SCOPE)
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_escape_identifier(_source_ident "${_source_db}")
cppbessot_test_pgsql_escape_identifier(_target_ident "${_target_db}")
cppbessot_test_pgsql_escape_literal(_source_lit "${_source_db}")
cppbessot_test_pgsql_escape_literal(_target_lit "${_target_db}")
cppbessot_test_shell_single_quote(_admin_shell "${CPPBESSOT_DB_ACTION_TEST_PGSQL_ADMIN_CONNSTR}")
cppbessot_test_shell_single_quote(
_term_target_shell
"SELECT pg_terminate_backend(pid) FROM pg_stat_activity WHERE datname = ${_target_lit} AND pid <> pg_backend_pid();")
cppbessot_test_shell_single_quote(
_term_source_shell
"SELECT pg_terminate_backend(pid) FROM pg_stat_activity WHERE datname = ${_source_lit} AND pid <> pg_backend_pid();")
cppbessot_test_shell_single_quote(_drop_shell "DROP DATABASE IF EXISTS ${_target_ident};")
cppbessot_test_shell_single_quote(_create_shell "CREATE DATABASE ${_target_ident} TEMPLATE ${_source_ident};")
set(${out_var}
"psql ${_admin_shell} -v ON_ERROR_STOP=1 -c ${_term_target_shell} && "
"psql ${_admin_shell} -v ON_ERROR_STOP=1 -c ${_term_source_shell} && "
"psql ${_admin_shell} -v ON_ERROR_STOP=1 -c ${_drop_shell} && "
"psql ${_admin_shell} -v ON_ERROR_STOP=1 -c ${_create_shell}"
PARENT_SCOPE)
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)
@@ -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,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()
@@ -1,11 +1,11 @@
include("${CMAKE_CURRENT_LIST_DIR}/../cmake/TestCommon.cmake")
cppbessot_test_require_real_pgsql_support()
cppbessot_test_require_var(CPPBESSOT_DB_PGSQL_DEV_CONNSTR)
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_DEV_CONNSTR}" "dev")
cppbessot_test_pgsql_isolated_connstr(_dev_connstr "${CPPBESSOT_DB_PGSQL_PRODDEV_CONNSTR}" "dev")
cppbessot_test_pgsql_reset_database("${_dev_connstr}")
cppbessot_test_pgsql_exec(
@@ -1,9 +1,9 @@
include("${CMAKE_CURRENT_LIST_DIR}/../cmake/TestCommon.cmake")
cppbessot_test_require_real_pgsql_support()
cppbessot_test_require_var(CPPBESSOT_DB_PGSQL_DEV_CONNSTR)
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_DEV_CONNSTR}" "dev")
cppbessot_test_pgsql_isolated_connstr(_dev_connstr "${CPPBESSOT_DB_PGSQL_PRODDEV_CONNSTR}" "dev")
cppbessot_test_pgsql_reset_database("${_dev_connstr}")
cppbessot_test_pgsql_exec(
@@ -1,9 +1,9 @@
include("${CMAKE_CURRENT_LIST_DIR}/../cmake/TestCommon.cmake")
cppbessot_test_require_real_pgsql_support()
cppbessot_test_require_var(CPPBESSOT_DB_PGSQL_DEV_CONNSTR)
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_DEV_CONNSTR}" "dev")
cppbessot_test_pgsql_isolated_connstr(_dev_connstr "${CPPBESSOT_DB_PGSQL_PRODDEV_CONNSTR}" "dev")
cppbessot_test_pgsql_reset_database("${_dev_connstr}")
cppbessot_test_pgsql_exec(
@@ -1,10 +1,9 @@
include("${CMAKE_CURRENT_LIST_DIR}/../cmake/TestCommon.cmake")
cppbessot_test_require_real_pgsql_support()
cppbessot_test_require_var(CPPBESSOT_DB_PGSQL_PROD_CONNSTR)
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_PROD_CONNSTR}" "prod")
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}")
@@ -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")
@@ -14,6 +14,15 @@ execute_process(
-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
@@ -21,5 +30,39 @@ execute_process(
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,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()
+8 -4
View File
@@ -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)
+2 -2
View File
@@ -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);
+2 -2
View File
@@ -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);