Skip to content

Commit 0bba8ba

Browse files
Pierre-Luc GagnéCopilot
andcommitted
Initial commit: DSMySQL header-only C++23 MySQL library
Extracted from DSQuantApp. Key changes: - Namespace renamed from dsquant_app to ds_mysql - All headers moved to lib/include/ds_mysql/ - Header-only library (INTERFACE target, no .cpp) - Removed dsquant/symbol.hpp (domain-specific to DSQuantApp) - Environment variables renamed: DS_MYSQL_TEST_* prefix - Docker container renamed: ds_mysql_test - New examples: basic_query.cpp, schema_generation.cpp - New README.md and docs/ Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
0 parents  commit 0bba8ba

52 files changed

Lines changed: 12756 additions & 0 deletions

Some content is hidden

Large Commits have some content hidden by default. Use the searchbox below for content that may be hidden.

.github/workflows/ci.yml

Lines changed: 127 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,127 @@
1+
name: CI
2+
3+
on:
4+
push:
5+
branches: [main]
6+
pull_request:
7+
branches: [main]
8+
workflow_dispatch:
9+
10+
jobs:
11+
unit-tests:
12+
runs-on: ubuntu-latest
13+
14+
steps:
15+
- uses: actions/checkout@v4
16+
17+
- name: Install dependencies
18+
run: |
19+
sudo apt-get update -q
20+
sudo apt-get install -y software-properties-common ca-certificates gpg wget
21+
# Kitware APT repo for CMake 3.31+
22+
wget -O - https://apt.kitware.com/keys/kitware-archive-latest.asc \
23+
| gpg --dearmor \
24+
| sudo tee /usr/share/keyrings/kitware-archive-keyring.gpg > /dev/null
25+
echo 'deb [signed-by=/usr/share/keyrings/kitware-archive-keyring.gpg] https://apt.kitware.com/ubuntu/ noble main' \
26+
| sudo tee /etc/apt/sources.list.d/kitware.list
27+
sudo add-apt-repository ppa:ubuntu-toolchain-r/test -y
28+
sudo apt-get update -q
29+
sudo apt-get install -y cmake ninja-build g++-15 \
30+
libmysqlclient-dev pkg-config
31+
32+
- name: Configure
33+
run: cmake --preset release -DSKIP_DOCKER_MANAGEMENT=ON -DBUILD_INTEGRATION_TESTS=OFF
34+
env:
35+
CXX: g++-15
36+
CC: gcc-15
37+
38+
- name: Build
39+
run: cmake --build build -j$(nproc)
40+
41+
- name: Test
42+
run: ctest --preset release
43+
44+
integration-tests:
45+
runs-on: ubuntu-latest
46+
47+
env:
48+
DS_MYSQL_TEST_HOST: 127.0.0.1
49+
DS_MYSQL_TEST_DATABASE: ds_mysql_test
50+
DS_MYSQL_TEST_USER: ds_mysql_test_user
51+
DS_MYSQL_TEST_PASSWORD: ds_mysql_test_password
52+
53+
steps:
54+
- uses: actions/checkout@v4
55+
56+
- name: Install dependencies
57+
run: |
58+
sudo apt-get update -q
59+
sudo apt-get install -y software-properties-common ca-certificates gpg wget
60+
# Kitware APT repo for CMake 3.31+
61+
wget -O - https://apt.kitware.com/keys/kitware-archive-latest.asc \
62+
| gpg --dearmor \
63+
| sudo tee /usr/share/keyrings/kitware-archive-keyring.gpg > /dev/null
64+
echo 'deb [signed-by=/usr/share/keyrings/kitware-archive-keyring.gpg] https://apt.kitware.com/ubuntu/ noble main' \
65+
| sudo tee /etc/apt/sources.list.d/kitware.list
66+
sudo add-apt-repository ppa:ubuntu-toolchain-r/test -y
67+
sudo apt-get update -q
68+
sudo apt-get install -y cmake ninja-build g++-15 \
69+
libmysqlclient-dev pkg-config
70+
71+
- name: Start MySQL
72+
run: |
73+
CONTAINER_ID=$(docker run -d \
74+
-p 0:3306 \
75+
-e MYSQL_ROOT_PASSWORD=root \
76+
-e MYSQL_DATABASE=ds_mysql_test \
77+
-e MYSQL_USER=ds_mysql_test_user \
78+
-e MYSQL_PASSWORD=ds_mysql_test_password \
79+
--health-cmd="mysqladmin ping -h localhost" \
80+
--health-interval=5s \
81+
--health-timeout=10s \
82+
--health-retries=10 \
83+
mysql:8.0)
84+
echo "MYSQL_CONTAINER_ID=$CONTAINER_ID" >> "$GITHUB_ENV"
85+
PORT=$(docker inspect --format='{{(index (index .NetworkSettings.Ports "3306/tcp") 0).HostPort}}' "$CONTAINER_ID")
86+
echo "DS_MYSQL_TEST_PORT=$PORT" >> "$GITHUB_ENV"
87+
echo "Waiting for MySQL to be healthy..."
88+
for i in $(seq 1 30); do
89+
STATUS=$(docker inspect --format='{{.State.Health.Status}}' "$CONTAINER_ID")
90+
if [ "$STATUS" = "healthy" ]; then
91+
echo "MySQL is healthy on port $PORT"
92+
break
93+
fi
94+
if [ "$i" = "30" ]; then
95+
echo "MySQL did not become healthy in time"
96+
docker logs "$CONTAINER_ID"
97+
exit 1
98+
fi
99+
echo "Status: $STATUS, waiting... ($i/30)"
100+
sleep 5
101+
done
102+
103+
- name: Initialize database
104+
run: |
105+
sudo apt-get install -y mysql-client
106+
mysql -h 127.0.0.1 -P "$DS_MYSQL_TEST_PORT" -u root -proot \
107+
< tests/integration/docker/init-db.sql
108+
109+
- name: Configure
110+
run: cmake --preset release -DSKIP_DOCKER_MANAGEMENT=ON
111+
env:
112+
CXX: g++-15
113+
CC: gcc-15
114+
115+
- name: Build
116+
run: cmake --build build -j$(nproc)
117+
118+
- name: Test
119+
run: ctest --preset release
120+
121+
- name: Stop MySQL
122+
if: always()
123+
run: |
124+
if [ -n "$MYSQL_CONTAINER_ID" ]; then
125+
docker stop "$MYSQL_CONTAINER_ID"
126+
docker rm "$MYSQL_CONTAINER_ID"
127+
fi

.gitignore

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,8 @@
1+
build/
2+
build-coverage/
3+
.env
4+
compile_commands.json
5+
*.o
6+
*.a
7+
*.so
8+
*.so.*

CMakeLists.txt

Lines changed: 152 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,152 @@
1+
cmake_minimum_required(VERSION 3.25)
2+
3+
project(DSMySQL
4+
VERSION 1.0.0
5+
DESCRIPTION "A portable C++23 type-safe MySQL query builder and database wrapper"
6+
LANGUAGES CXX
7+
)
8+
9+
# Configure git hooks at cmake configure time
10+
if(EXISTS "${CMAKE_SOURCE_DIR}/.githooks")
11+
execute_process(
12+
COMMAND git config core.hooksPath .githooks
13+
WORKING_DIRECTORY ${CMAKE_SOURCE_DIR}
14+
)
15+
endif()
16+
17+
# Set C++23 standard
18+
set(CMAKE_CXX_STANDARD 23)
19+
set(CMAKE_CXX_STANDARD_REQUIRED ON)
20+
set(CMAKE_CXX_EXTENSIONS OFF)
21+
22+
# Export compile commands for IDEs
23+
set(CMAKE_EXPORT_COMPILE_COMMANDS ON)
24+
25+
# Set default build type if not specified
26+
if(NOT CMAKE_BUILD_TYPE)
27+
set(CMAKE_BUILD_TYPE Release)
28+
endif()
29+
30+
# Build options
31+
option(BUILD_EXAMPLES "Build examples" ON)
32+
option(ENABLE_COVERAGE "Enable code coverage" OFF)
33+
option(SKIP_DOCKER_MANAGEMENT "Skip Docker container lifecycle in CTest (use when DB is externally provided, e.g. CI)" OFF)
34+
option(BUILD_INTEGRATION_TESTS "Build integration tests" ON)
35+
36+
# Compiler options
37+
if(CMAKE_CXX_COMPILER_ID MATCHES "GNU|Clang")
38+
add_compile_options(
39+
-Wall
40+
-Wextra
41+
-Wpedantic
42+
-Werror
43+
)
44+
endif()
45+
46+
# Coverage flags
47+
if(ENABLE_COVERAGE)
48+
if(CMAKE_CXX_COMPILER_ID MATCHES "GNU|Clang")
49+
add_compile_options(--coverage)
50+
add_link_options(--coverage)
51+
message(STATUS "Coverage enabled with --coverage flags")
52+
else()
53+
message(WARNING "Coverage is not supported with ${CMAKE_CXX_COMPILER_ID}")
54+
endif()
55+
endif()
56+
57+
# Include FetchContent for dependency management
58+
include(FetchContent)
59+
60+
# Make local CMake helper modules available early (needed by fetched deps).
61+
list(APPEND CMAKE_MODULE_PATH "${CMAKE_CURRENT_SOURCE_DIR}/cmake")
62+
63+
# MySQL client library
64+
find_package(PkgConfig REQUIRED)
65+
pkg_check_modules(MYSQL REQUIRED mysqlclient)
66+
67+
# Note: Boost.PFR is header-only and doesn't have a standard CMake config package yet,
68+
# so we fetch it directly without find_package. Once Boost.PFR is officially integrated
69+
# into Boost's CMake build system, we can use: find_package(Boost REQUIRED COMPONENTS pfr CONFIG)
70+
71+
# Boost.PFR (header-only, for compile-time reflection without macros)
72+
# We only need the headers; FetchContent_Populate avoids processing Boost.PFR's own
73+
# CMake build system, whose tests require Boost::core which is unavailable here.
74+
FetchContent_Declare(
75+
boost_pfr_headers
76+
GIT_REPOSITORY https://github.com/boostorg/pfr.git
77+
GIT_TAG master
78+
GIT_SHALLOW TRUE
79+
)
80+
# CMP0169 OLD: suppress the single-arg FetchContent_Populate deprecation warning.
81+
# This is intentional — we want headers only, not add_subdirectory behaviour.
82+
cmake_policy(PUSH)
83+
if(POLICY CMP0169)
84+
cmake_policy(SET CMP0169 OLD)
85+
endif()
86+
FetchContent_Populate(boost_pfr_headers)
87+
cmake_policy(POP)
88+
89+
# Create Boost.PFR target if not already provided by Boost.PFR's own CMake
90+
if(NOT TARGET Boost::pfr)
91+
add_library(Boost::pfr INTERFACE IMPORTED)
92+
set_target_properties(Boost::pfr PROPERTIES
93+
INTERFACE_INCLUDE_DIRECTORIES "${boost_pfr_headers_SOURCE_DIR}/include"
94+
INTERFACE_SYSTEM_INCLUDE_DIRECTORIES "${boost_pfr_headers_SOURCE_DIR}/include"
95+
)
96+
endif()
97+
98+
# Auto-create .env from .env.example in tests/integration if missing
99+
if(NOT EXISTS "${CMAKE_SOURCE_DIR}/tests/integration/.env")
100+
if(EXISTS "${CMAKE_SOURCE_DIR}/tests/integration/.env.example")
101+
configure_file(
102+
"${CMAKE_SOURCE_DIR}/tests/integration/.env.example"
103+
"${CMAKE_SOURCE_DIR}/tests/integration/.env"
104+
COPYONLY
105+
)
106+
message(STATUS "✓ Created tests/integration/.env from .env.example")
107+
endif()
108+
endif()
109+
110+
# Install git hooks on configure
111+
if(EXISTS "${CMAKE_SOURCE_DIR}/.githooks")
112+
file(COPY ${CMAKE_SOURCE_DIR}/.githooks/
113+
DESTINATION ${CMAKE_SOURCE_DIR}/.git/hooks
114+
FILE_PERMISSIONS OWNER_READ OWNER_WRITE OWNER_EXECUTE)
115+
message(STATUS "✓ Git hooks installed")
116+
endif()
117+
118+
# Enable testing via CTest (defines BUILD_TESTING)
119+
include(CTest)
120+
121+
# Setup code coverage if enabled
122+
if(ENABLE_COVERAGE)
123+
include(CodeCoverage)
124+
endif()
125+
126+
# Add library
127+
add_subdirectory(lib)
128+
129+
# Add tests
130+
if(BUILD_TESTING)
131+
add_subdirectory(tests)
132+
endif()
133+
134+
# Add examples
135+
if(BUILD_EXAMPLES)
136+
add_subdirectory(examples)
137+
endif()
138+
139+
# Display configuration summary
140+
message(STATUS "")
141+
message(STATUS "===== DSMySQL Configuration =====")
142+
message(STATUS "Build type: ${CMAKE_BUILD_TYPE}")
143+
message(STATUS "C++ compiler: ${CMAKE_CXX_COMPILER_ID} ${CMAKE_CXX_COMPILER_VERSION}")
144+
message(STATUS "C++ standard: C++${CMAKE_CXX_STANDARD}")
145+
message(STATUS "Build tests: ${BUILD_TESTING}")
146+
message(STATUS "Build integration tests: ${BUILD_INTEGRATION_TESTS}")
147+
message(STATUS "Skip Docker management: ${SKIP_DOCKER_MANAGEMENT}")
148+
message(STATUS "Build examples: ${BUILD_EXAMPLES}")
149+
message(STATUS "Enable coverage: ${ENABLE_COVERAGE}")
150+
message(STATUS "Output directory: ${CMAKE_BINARY_DIR}")
151+
message(STATUS "================================")
152+
message(STATUS "")

0 commit comments

Comments
 (0)