# Implementation Plan: Issue #192 — Database Portability ## Summary Replace 5 MySQL-specific SQL expressions with platform-aware equivalents using a new PlatformHelper utility class, declare PostgreSQL and SQLite support in info.xml, add Docker infrastructure for all three backends, create integration tests, and set up CI pipeline. ## Phase 1: Core Portability Fixes 1. Create `lib/Db/PlatformHelper.php` with 3 methods: - `getYearDiffExpression($db, $col)` — age/duration calculation - `getMonthExpression($db, $col)` — extract month - `getYearExpression($db, $col)` — extract year 2. Update `lib/Service/QueryService.php` lines 300, 318 — use PlatformHelper 3. Update `lib/Db/MemberMapper.php` lines 165, 301 — use PlatformHelper 4. Update `lib/Db/LagerMapper.php` line 70 — use PlatformHelper 5. Update `appinfo/info.xml` — add pgsql and sqlite database declarations ## Phase 2: Docker Infrastructure 6. Create `docker/postgres/docker-compose.yml` 7. Create `docker/sqlite/docker-compose.yml` 8. Add Makefile targets for postgres and sqlite ## Phase 3: Integration Tests 9. Create `tests/DatabasePortability/PlatformHelperTest.php` ## Phase 4: CI Pipeline 10. Create `.gitea/workflows/database-portability.yml` ## Phase 5: Security Review 11. Verify PlatformHelper introduces no SQL injection vectors 12. Verify column expressions are hardcoded, not user-supplied ## AC Verification Checklist 1. [ ] appinfo/info.xml declares mysql, pgsql, and sqlite 2. [ ] No MySQL-specific SQL remains (no TIMESTAMPDIFF, CURDATE, LIKE on date columns) 3. [ ] PlatformHelper generates correct expressions for all 3 backends 4. [ ] Age/duration filters use PlatformHelper 5. [ ] Birthday-this-month filter uses PlatformHelper 6. [ ] Lager findByYear uses PlatformHelper 7. [ ] Docker Compose files exist for MySQL, PostgreSQL, and SQLite 8. [ ] Integration test suite exists 9. [ ] CI pipeline definition exists 10. [ ] No SQL injection vectors introduced (all expressions use hardcoded column names)