fix(project): replace CAST(... AS int) with CASE WHEN for MySQL 8 compat

MySQL 8 rejects CAST(... AS int) (only SIGNED/UNSIGNED/CHAR/... are
accepted as target types), causing /api/v1/projects, /api/v1/tasks,
and /api/v1/labels to return HTTP 500 for every authenticated user on
MySQL 8. SQLite, Postgres, and MariaDB lax mode silently accepted the
expression, which is why the regression (introduced in e3045dfd0,
shipped in v2.3.0) passed CI — the mysql CI matrix leg uses
mariadb:12, not real MySQL 8.

Replace the two CAST(all_projects.is_archived AS int) expressions in
the recursive project CTE with MAX(CASE WHEN ... THEN 1 ELSE 0 END),
which is dialect-agnostic and needs no cast on any supported backend.

Fixes #2589
This commit is contained in:
kolaente 2026-04-11 18:17:22 +02:00 committed by kolaente
parent 3b7996feef
commit 5b2cbcb1b5
1 changed files with 2 additions and 2 deletions

View File

@ -582,7 +582,7 @@ INNER JOIN all_projects ap ON p.parent_project_id = ap.id`
"all_projects.hex_color",
"all_projects.owner_id",
"CASE WHEN all_projects.parent_project_id IS NULL THEN 0 ELSE all_projects.parent_project_id END AS parent_project_id",
"MAX(CAST(all_projects.is_archived AS int)) AS is_archived",
"MAX(CASE WHEN all_projects.is_archived THEN 1 ELSE 0 END) AS is_archived",
"all_projects.background_file_id",
"all_projects.background_blur_hash",
"all_projects.position",
@ -607,7 +607,7 @@ INNER JOIN all_projects ap ON p.parent_project_id = ap.id`
var archivedFilter string
if !opts.getArchived {
archivedFilter = "HAVING MAX(CAST(all_projects.is_archived AS int)) = 0 "
archivedFilter = "HAVING MAX(CASE WHEN all_projects.is_archived THEN 1 ELSE 0 END) = 0 "
}
currentProjects := []*Project{}