Database portability: support PostgreSQL and SQLite alongside MySQL/MariaDB #192
Reference in New Issue
Block a user
Delete Branch "%!s()"
Deleting a branch is permanent. Although the deleted branch may continue to exist for a short time before it actually gets removed, it CANNOT be undone in most cases. Continue?
Summary
The app currently only works with MySQL/MariaDB (
appinfo/info.xmldeclares only<database>mysql</database>). Nextcloud officially supports PostgreSQL and SQLite as well. This issue covers making the app fully portable across all three database backends.Current State
The codebase already uses Nextcloud's OCP database abstraction (QBMapper, IQueryBuilder, ISchemaWrapper) for ~98% of database operations. Only 5 specific locations contain MySQL-specific SQL:
MySQL-Specific Code
lib/Service/QueryService.phpTIMESTAMPDIFF(YEAR, m.geburtsdatum, CURDATE())— MySQL-only function for age calculationlib/Service/QueryService.phpTIMESTAMPDIFF(YEAR, m.eintritt, CURDATE())— MySQL-only function for membership durationlib/Db/MemberMapper.phpLIKEon DATE column — PostgreSQL doesn't supportLIKEon date-typed columnslib/Db/MemberMapper.phpLIKEon DATE column infindFiltered()lib/Db/LagerMapper.phpLIKEon DATE column for year filteringAlready Portable (no changes needed)
OCP\DB\TypesconstantsiLike(),escapeLikeParameter(), COUNT(), JOINs — all through query builderunsigned: trueon BIGINT columns — Doctrine DBAL silently ignores this on PostgreSQL/SQLiteImplementation Plan
Phase 1: Core Portability Fixes
Create
lib/Db/PlatformHelper.php— a thin utility with 3 methods that useIDBConnection::getDatabaseProvider()to generate platform-specific SQL:getYearDiffExpression($db, $col)— year difference between date column and todayTIMESTAMPDIFF(YEAR, col, CURDATE())EXTRACT(YEAR FROM AGE(CURRENT_DATE, col))::integergetMonthExpression($db, $col)— extract month from date columnMONTH(col)/ PostgreSQL:EXTRACT(MONTH FROM col)/ SQLite:strftime('%m', col)getYearExpression($db, $col)— extract year from date columnYEAR(col)/ PostgreSQL:EXTRACT(YEAR FROM col)/ SQLite:strftime('%Y', col)Update affected files:
QueryService.php— usegetYearDiffExpression()instead ofTIMESTAMPDIFF/CURDATEMemberMapper.php— usegetMonthExpression()instead ofLIKE '%-MM-%'LagerMapper.php— usegetYearExpression()instead ofLIKE 'YYYY-%'appinfo/info.xml— add<database>pgsql</database>and<database>sqlite</database>Phase 2: Docker Infrastructure
Create Docker Compose files for each database variant:
Add Makefile targets:
deploy-mysql,deploy-postgres,deploy-sqlitewith matchingsetup-*targets.Phase 3: Integration Tests
Create
tests/DatabasePortability/test suite that runs against real databases (no mocks):MigrationTestMemberMapperTestQueryServiceTestLagerMapperTestFeeRecordMapperTestAuditLogMapperTestUnicodeTestCountQueryTestPhase 4: CI Pipeline
.gitea/workflows/database-portability.ymlwith matrix strategy over[mysql, postgres, sqlite].Phase 5: Security Review
Acceptance Criteria
appinfo/info.xmldeclares all three databases