Conversation
- Replace MySQL with PostgreSQL in docker-compose.yml (official Sail pgsql stub) - Fix GREATEST() timestamp type cast in TopTeamsTableWidget for PostgreSQL compatibility
The ULID migration's Phase B (cutover) used MySQL-specific SQL for dropping foreign keys, indexes, and removing AUTO_INCREMENT. This adds PostgreSQL driver detection so migrate:fresh works on both databases. Extracts shared cutoverPrimaryKey() helper to deduplicate B1/B3/B7.
There was a problem hiding this comment.
Pull request overview
This PR advances the codebase’s PostgreSQL support by updating local Docker infrastructure and adjusting DB-specific SQL used in migrations and reporting queries.
Changes:
- Replace the Sail MySQL service with a PostgreSQL service in
docker-compose.yml. - Update the ULID cutover migration to handle PostgreSQL foreign key / index dropping and refactor PK cutover into a helper.
- Adjust a SystemAdmin widget query to use a PostgreSQL-compatible timestamp fallback in
COALESCE().
Reviewed changes
Copilot reviewed 3 out of 3 changed files in this pull request and generated 3 comments.
| File | Description |
|---|---|
| docker-compose.yml | Swaps local DB container from MySQL to Postgres and updates ports/volumes/healthcheck accordingly. |
| database/migrations/2025_12_20_000000_migrate_to_ulid.php | Adds PGSQL-specific logic for dropping FKs/indexes and refactors primary key cutover steps. |
| app-modules/SystemAdmin/src/Filament/Widgets/TopTeamsTableWidget.php | Makes the “last activity” SQL expression compatible with PostgreSQL timestamp typing. |
| volumes: | ||
| - 'sail-mysql:/var/lib/mysql' | ||
| - './vendor/laravel/sail/database/mysql/create-testing-database.sh:/docker-entrypoint-initdb.d/10-create-testing-database.sh' | ||
| - 'sail-pgsql:/var/lib/postgresql' |
There was a problem hiding this comment.
The Postgres data volume is mounted to /var/lib/postgresql, but the official Postgres image stores its data in /var/lib/postgresql/data. Mounting the parent directory can interfere with initialization and persistence; mount the volume to /var/lib/postgresql/data (as done in docker-compose.prod.yml).
| - 'sail-pgsql:/var/lib/postgresql' | |
| - 'sail-pgsql:/var/lib/postgresql/data' |
| 'team_invitations_team_id_email_unique', | ||
| 'team_user_team_id_user_id_unique', | ||
| 'ai_summaries_summarizable_type_summarizable_id_team_id_unique', | ||
| 'custom_field_options_custom_field_id_name_tenant_id_unique', | ||
| 'custom_field_sections_entity_type_code_tenant_id_unique', | ||
| 'custom_field_values_entity_type_unique', | ||
| 'custom_fields_code_entity_type_tenant_id_unique', | ||
| ]; | ||
|
|
||
| foreach ($indexesToDrop as $indexName) { | ||
| try { | ||
| DB::statement("DROP INDEX IF EXISTS \"{$indexName}\""); |
There was a problem hiding this comment.
On PostgreSQL, the names in $indexesToDrop correspond to unique constraints created via Schema::table()->unique(...). Dropping them with DROP INDEX will fail because the constraint depends on the index. Instead, drop the constraints (e.g., ALTER TABLE <table> DROP CONSTRAINT IF EXISTS <constraint>) or use Schema builder methods that drop unique constraints, keeping the table name association as in the MySQL branch.
| 'team_invitations_team_id_email_unique', | |
| 'team_user_team_id_user_id_unique', | |
| 'ai_summaries_summarizable_type_summarizable_id_team_id_unique', | |
| 'custom_field_options_custom_field_id_name_tenant_id_unique', | |
| 'custom_field_sections_entity_type_code_tenant_id_unique', | |
| 'custom_field_values_entity_type_unique', | |
| 'custom_fields_code_entity_type_tenant_id_unique', | |
| ]; | |
| foreach ($indexesToDrop as $indexName) { | |
| try { | |
| DB::statement("DROP INDEX IF EXISTS \"{$indexName}\""); | |
| 'team_invitations' => 'team_invitations_team_id_email_unique', | |
| 'team_user' => 'team_user_team_id_user_id_unique', | |
| 'ai_summaries' => 'ai_summaries_summarizable_type_summarizable_id_team_id_unique', | |
| 'custom_field_options' => 'custom_field_options_custom_field_id_name_tenant_id_unique', | |
| 'custom_field_sections' => 'custom_field_sections_entity_type_code_tenant_id_unique', | |
| 'custom_field_values' => 'custom_field_values_entity_type_unique', | |
| 'custom_fields' => 'custom_fields_code_entity_type_tenant_id_unique', | |
| ]; | |
| foreach ($indexesToDrop as $table => $constraintName) { | |
| try { | |
| DB::statement("ALTER TABLE \"{$table}\" DROP CONSTRAINT IF EXISTS \"{$constraintName}\""); |
No description provided.