From d35c9400b04ee53f5aca4b1638f6a033daf8e00b Mon Sep 17 00:00:00 2001 From: klouds27 Date: Thu, 28 May 2026 00:48:44 +0200 Subject: [PATCH 1/2] fix: prevent type mismatch when deleting empty swimlane When deleting a swimlane with no user stories, all swimlane_id values in the bulk update data could be None. PostgreSQL infers text type for a VALUES column that is entirely NULL, causing a DatatypeMismatch error against the integer swimlane_id column. Add an explicit ::integer cast via the template parameter of execute_values so PostgreSQL resolves the correct type regardless of whether sid values are NULL or integer. Also guard against an empty data list to skip the update when there are no user stories to reorder. Fixes #197 Signed-off-by: klouds27 --- taiga/projects/services/bulk_update_order.py | 6 +++++- 1 file changed, 5 insertions(+), 1 deletion(-) diff --git a/taiga/projects/services/bulk_update_order.py b/taiga/projects/services/bulk_update_order.py index a7626a521..47e425b8c 100644 --- a/taiga/projects/services/bulk_update_order.py +++ b/taiga/projects/services/bulk_update_order.py @@ -287,6 +287,9 @@ def update_order_and_swimlane(swimlane_to_be_deleted, move_to_swimlane): new_indexes = range(0, len(ordered_uss_ids)) data = list(zip(ordered_swimlane_ids, ordered_uss_ids, new_indexes)) + if not data: + return + with connection.cursor() as curs: execute_values(curs, """ @@ -295,4 +298,5 @@ def update_order_and_swimlane(swimlane_to_be_deleted, move_to_swimlane): swimlane_id = tmp.sid FROM (VALUES %s) AS tmp (sid, ussid, new_order) WHERE tmp.ussid = userstories_userstory.id""", - data) + data, + template="(%s::integer, %s, %s)") From 832ce5519a366d0174d80fe84d8df1f33965ffcd Mon Sep 17 00:00:00 2001 From: klouds27 Date: Thu, 28 May 2026 01:09:53 +0200 Subject: [PATCH 2/2] fix: cast all execute_values template fields to integer Add explicit ::integer casts for ussid and new_order in the execute_values template, matching the types of the target columns (userstories_userstory.id and kanban_order). Makes the query self-documenting and avoids implicit cast reliance. Signed-off-by: klouds27 --- taiga/projects/services/bulk_update_order.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/taiga/projects/services/bulk_update_order.py b/taiga/projects/services/bulk_update_order.py index 47e425b8c..9855340d1 100644 --- a/taiga/projects/services/bulk_update_order.py +++ b/taiga/projects/services/bulk_update_order.py @@ -299,4 +299,4 @@ def update_order_and_swimlane(swimlane_to_be_deleted, move_to_swimlane): FROM (VALUES %s) AS tmp (sid, ussid, new_order) WHERE tmp.ussid = userstories_userstory.id""", data, - template="(%s::integer, %s, %s)") + template="(%s::integer, %s::integer, %s::integer)")