Skip to content

Commit 7618ae7

Browse files
authored
Merge pull request #260 from Worklenz/fix/release-v.2.1.1
Fix/release v.2.1.1
2 parents 6c08f10 + 8087313 commit 7618ae7

551 files changed

Lines changed: 24304 additions & 3500 deletions

File tree

Some content is hidden

Large Commits have some content hidden by default. Use the searchbox below for content that may be hidden.

README.md

Lines changed: 1 addition & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -69,8 +69,7 @@ cd worklenz
6969
2. Set up environment variables
7070
- Copy the example environment files
7171
```bash
72-
cp .env.example .env
73-
cp worklenz-backend/.env.example worklenz-backend/.env
72+
cp worklenz-backend/.env.template worklenz-backend/.env
7473
```
7574
- Update the environment variables with your configuration
7675

Lines changed: 143 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,143 @@
1+
-- Fix window function error in task sort optimized functions
2+
-- Error: window functions are not allowed in UPDATE
3+
4+
-- Replace the optimized sort functions to avoid CTE usage in UPDATE statements
5+
CREATE OR REPLACE FUNCTION handle_task_list_sort_between_groups_optimized(_from_index integer, _to_index integer, _task_id uuid, _project_id uuid, _batch_size integer DEFAULT 100) RETURNS void
6+
LANGUAGE plpgsql
7+
AS
8+
$$
9+
DECLARE
10+
_offset INT := 0;
11+
_affected_rows INT;
12+
BEGIN
13+
-- PERFORMANCE OPTIMIZATION: Use direct updates without CTE in UPDATE
14+
IF (_to_index = -1)
15+
THEN
16+
_to_index = COALESCE((SELECT MAX(sort_order) + 1 FROM tasks WHERE project_id = _project_id), 0);
17+
END IF;
18+
19+
-- PERFORMANCE OPTIMIZATION: Batch updates for large datasets
20+
IF _to_index > _from_index
21+
THEN
22+
LOOP
23+
UPDATE tasks
24+
SET sort_order = sort_order - 1
25+
WHERE project_id = _project_id
26+
AND sort_order > _from_index
27+
AND sort_order < _to_index
28+
AND sort_order > _offset
29+
AND sort_order <= _offset + _batch_size;
30+
31+
GET DIAGNOSTICS _affected_rows = ROW_COUNT;
32+
EXIT WHEN _affected_rows = 0;
33+
_offset := _offset + _batch_size;
34+
END LOOP;
35+
36+
UPDATE tasks SET sort_order = _to_index - 1 WHERE id = _task_id AND project_id = _project_id;
37+
END IF;
38+
39+
IF _to_index < _from_index
40+
THEN
41+
_offset := 0;
42+
LOOP
43+
UPDATE tasks
44+
SET sort_order = sort_order + 1
45+
WHERE project_id = _project_id
46+
AND sort_order > _to_index
47+
AND sort_order < _from_index
48+
AND sort_order > _offset
49+
AND sort_order <= _offset + _batch_size;
50+
51+
GET DIAGNOSTICS _affected_rows = ROW_COUNT;
52+
EXIT WHEN _affected_rows = 0;
53+
_offset := _offset + _batch_size;
54+
END LOOP;
55+
56+
UPDATE tasks SET sort_order = _to_index + 1 WHERE id = _task_id AND project_id = _project_id;
57+
END IF;
58+
END
59+
$$;
60+
61+
-- Replace the second optimized sort function
62+
CREATE OR REPLACE FUNCTION handle_task_list_sort_inside_group_optimized(_from_index integer, _to_index integer, _task_id uuid, _project_id uuid, _batch_size integer DEFAULT 100) RETURNS void
63+
LANGUAGE plpgsql
64+
AS
65+
$$
66+
DECLARE
67+
_offset INT := 0;
68+
_affected_rows INT;
69+
BEGIN
70+
-- PERFORMANCE OPTIMIZATION: Batch updates for large datasets without CTE in UPDATE
71+
IF _to_index > _from_index
72+
THEN
73+
LOOP
74+
UPDATE tasks
75+
SET sort_order = sort_order - 1
76+
WHERE project_id = _project_id
77+
AND sort_order > _from_index
78+
AND sort_order <= _to_index
79+
AND sort_order > _offset
80+
AND sort_order <= _offset + _batch_size;
81+
82+
GET DIAGNOSTICS _affected_rows = ROW_COUNT;
83+
EXIT WHEN _affected_rows = 0;
84+
_offset := _offset + _batch_size;
85+
END LOOP;
86+
END IF;
87+
88+
IF _to_index < _from_index
89+
THEN
90+
_offset := 0;
91+
LOOP
92+
UPDATE tasks
93+
SET sort_order = sort_order + 1
94+
WHERE project_id = _project_id
95+
AND sort_order >= _to_index
96+
AND sort_order < _from_index
97+
AND sort_order > _offset
98+
AND sort_order <= _offset + _batch_size;
99+
100+
GET DIAGNOSTICS _affected_rows = ROW_COUNT;
101+
EXIT WHEN _affected_rows = 0;
102+
_offset := _offset + _batch_size;
103+
END LOOP;
104+
END IF;
105+
106+
UPDATE tasks SET sort_order = _to_index WHERE id = _task_id AND project_id = _project_id;
107+
END
108+
$$;
109+
110+
-- Add simple bulk update function as alternative
111+
CREATE OR REPLACE FUNCTION update_task_sort_orders_bulk(_updates json) RETURNS void
112+
LANGUAGE plpgsql
113+
AS
114+
$$
115+
DECLARE
116+
_update_record RECORD;
117+
BEGIN
118+
-- Simple approach: update each task's sort_order from the provided array
119+
FOR _update_record IN
120+
SELECT
121+
(item->>'task_id')::uuid as task_id,
122+
(item->>'sort_order')::int as sort_order,
123+
(item->>'status_id')::uuid as status_id,
124+
(item->>'priority_id')::uuid as priority_id,
125+
(item->>'phase_id')::uuid as phase_id
126+
FROM json_array_elements(_updates) as item
127+
LOOP
128+
UPDATE tasks
129+
SET
130+
sort_order = _update_record.sort_order,
131+
status_id = COALESCE(_update_record.status_id, status_id),
132+
priority_id = COALESCE(_update_record.priority_id, priority_id)
133+
WHERE id = _update_record.task_id;
134+
135+
-- Handle phase updates separately since it's in a different table
136+
IF _update_record.phase_id IS NOT NULL THEN
137+
INSERT INTO task_phase (task_id, phase_id)
138+
VALUES (_update_record.task_id, _update_record.phase_id)
139+
ON CONFLICT (task_id) DO UPDATE SET phase_id = _update_record.phase_id;
140+
END IF;
141+
END LOOP;
142+
END
143+
$$;

worklenz-backend/database/sql/4_functions.sql

Lines changed: 99 additions & 46 deletions
Original file line numberDiff line numberDiff line change
@@ -5497,8 +5497,15 @@ $$
54975497
DECLARE
54985498
_iterator NUMERIC := 0;
54995499
_status_id TEXT;
5500+
_project_id UUID;
5501+
_base_sort_order NUMERIC;
55005502
BEGIN
5503+
-- Get the project_id from the first status to ensure we update all statuses in the same project
5504+
SELECT project_id INTO _project_id
5505+
FROM task_statuses
5506+
WHERE id = (SELECT TRIM(BOTH '"' FROM JSON_ARRAY_ELEMENTS(_status_ids)::TEXT) LIMIT 1)::UUID;
55015507

5508+
-- Update the sort_order for statuses in the provided order
55025509
FOR _status_id IN SELECT * FROM JSON_ARRAY_ELEMENTS((_status_ids)::JSON)
55035510
LOOP
55045511
UPDATE task_statuses
@@ -5507,6 +5514,29 @@ BEGIN
55075514
_iterator := _iterator + 1;
55085515
END LOOP;
55095516

5517+
-- Get the base sort order for remaining statuses (simple count approach)
5518+
SELECT COUNT(*) INTO _base_sort_order
5519+
FROM task_statuses ts2
5520+
WHERE ts2.project_id = _project_id
5521+
AND ts2.id = ANY(SELECT (TRIM(BOTH '"' FROM JSON_ARRAY_ELEMENTS(_status_ids)::TEXT))::UUID);
5522+
5523+
-- Update remaining statuses with simple sequential numbering
5524+
-- Reset iterator to start from base_sort_order
5525+
_iterator := _base_sort_order;
5526+
5527+
-- Use a cursor approach to avoid window functions
5528+
FOR _status_id IN
5529+
SELECT id::TEXT FROM task_statuses
5530+
WHERE project_id = _project_id
5531+
AND id NOT IN (SELECT (TRIM(BOTH '"' FROM JSON_ARRAY_ELEMENTS(_status_ids)::TEXT))::UUID)
5532+
ORDER BY sort_order
5533+
LOOP
5534+
UPDATE task_statuses
5535+
SET sort_order = _iterator
5536+
WHERE id = _status_id::UUID;
5537+
_iterator := _iterator + 1;
5538+
END LOOP;
5539+
55105540
RETURN;
55115541
END
55125542
$$;
@@ -6394,7 +6424,7 @@ DECLARE
63946424
_offset INT := 0;
63956425
_affected_rows INT;
63966426
BEGIN
6397-
-- PERFORMANCE OPTIMIZATION: Use CTE for better query planning
6427+
-- PERFORMANCE OPTIMIZATION: Use direct updates without CTE in UPDATE
63986428
IF (_to_index = -1)
63996429
THEN
64006430
_to_index = COALESCE((SELECT MAX(sort_order) + 1 FROM tasks WHERE project_id = _project_id), 0);
@@ -6404,18 +6434,15 @@ BEGIN
64046434
IF _to_index > _from_index
64056435
THEN
64066436
LOOP
6407-
WITH batch_update AS (
6408-
UPDATE tasks
6409-
SET sort_order = sort_order - 1
6410-
WHERE project_id = _project_id
6411-
AND sort_order > _from_index
6412-
AND sort_order < _to_index
6413-
AND sort_order > _offset
6414-
AND sort_order <= _offset + _batch_size
6415-
RETURNING 1
6416-
)
6417-
SELECT COUNT(*) INTO _affected_rows FROM batch_update;
6437+
UPDATE tasks
6438+
SET sort_order = sort_order - 1
6439+
WHERE project_id = _project_id
6440+
AND sort_order > _from_index
6441+
AND sort_order < _to_index
6442+
AND sort_order > _offset
6443+
AND sort_order <= _offset + _batch_size;
64186444

6445+
GET DIAGNOSTICS _affected_rows = ROW_COUNT;
64196446
EXIT WHEN _affected_rows = 0;
64206447
_offset := _offset + _batch_size;
64216448
END LOOP;
@@ -6427,18 +6454,15 @@ BEGIN
64276454
THEN
64286455
_offset := 0;
64296456
LOOP
6430-
WITH batch_update AS (
6431-
UPDATE tasks
6432-
SET sort_order = sort_order + 1
6433-
WHERE project_id = _project_id
6434-
AND sort_order > _to_index
6435-
AND sort_order < _from_index
6436-
AND sort_order > _offset
6437-
AND sort_order <= _offset + _batch_size
6438-
RETURNING 1
6439-
)
6440-
SELECT COUNT(*) INTO _affected_rows FROM batch_update;
6457+
UPDATE tasks
6458+
SET sort_order = sort_order + 1
6459+
WHERE project_id = _project_id
6460+
AND sort_order > _to_index
6461+
AND sort_order < _from_index
6462+
AND sort_order > _offset
6463+
AND sort_order <= _offset + _batch_size;
64416464

6465+
GET DIAGNOSTICS _affected_rows = ROW_COUNT;
64426466
EXIT WHEN _affected_rows = 0;
64436467
_offset := _offset + _batch_size;
64446468
END LOOP;
@@ -6457,22 +6481,19 @@ DECLARE
64576481
_offset INT := 0;
64586482
_affected_rows INT;
64596483
BEGIN
6460-
-- PERFORMANCE OPTIMIZATION: Batch updates for large datasets
6484+
-- PERFORMANCE OPTIMIZATION: Batch updates for large datasets without CTE in UPDATE
64616485
IF _to_index > _from_index
64626486
THEN
64636487
LOOP
6464-
WITH batch_update AS (
6465-
UPDATE tasks
6466-
SET sort_order = sort_order - 1
6467-
WHERE project_id = _project_id
6468-
AND sort_order > _from_index
6469-
AND sort_order <= _to_index
6470-
AND sort_order > _offset
6471-
AND sort_order <= _offset + _batch_size
6472-
RETURNING 1
6473-
)
6474-
SELECT COUNT(*) INTO _affected_rows FROM batch_update;
6488+
UPDATE tasks
6489+
SET sort_order = sort_order - 1
6490+
WHERE project_id = _project_id
6491+
AND sort_order > _from_index
6492+
AND sort_order <= _to_index
6493+
AND sort_order > _offset
6494+
AND sort_order <= _offset + _batch_size;
64756495

6496+
GET DIAGNOSTICS _affected_rows = ROW_COUNT;
64766497
EXIT WHEN _affected_rows = 0;
64776498
_offset := _offset + _batch_size;
64786499
END LOOP;
@@ -6482,18 +6503,15 @@ BEGIN
64826503
THEN
64836504
_offset := 0;
64846505
LOOP
6485-
WITH batch_update AS (
6486-
UPDATE tasks
6487-
SET sort_order = sort_order + 1
6488-
WHERE project_id = _project_id
6489-
AND sort_order >= _to_index
6490-
AND sort_order < _from_index
6491-
AND sort_order > _offset
6492-
AND sort_order <= _offset + _batch_size
6493-
RETURNING 1
6494-
)
6495-
SELECT COUNT(*) INTO _affected_rows FROM batch_update;
6506+
UPDATE tasks
6507+
SET sort_order = sort_order + 1
6508+
WHERE project_id = _project_id
6509+
AND sort_order >= _to_index
6510+
AND sort_order < _from_index
6511+
AND sort_order > _offset
6512+
AND sort_order <= _offset + _batch_size;
64966513

6514+
GET DIAGNOSTICS _affected_rows = ROW_COUNT;
64976515
EXIT WHEN _affected_rows = 0;
64986516
_offset := _offset + _batch_size;
64996517
END LOOP;
@@ -6502,3 +6520,38 @@ BEGIN
65026520
UPDATE tasks SET sort_order = _to_index WHERE id = _task_id AND project_id = _project_id;
65036521
END
65046522
$$;
6523+
6524+
-- Simple function to update task sort orders in bulk
6525+
CREATE OR REPLACE FUNCTION update_task_sort_orders_bulk(_updates json) RETURNS void
6526+
LANGUAGE plpgsql
6527+
AS
6528+
$$
6529+
DECLARE
6530+
_update_record RECORD;
6531+
BEGIN
6532+
-- Simple approach: update each task's sort_order from the provided array
6533+
FOR _update_record IN
6534+
SELECT
6535+
(item->>'task_id')::uuid as task_id,
6536+
(item->>'sort_order')::int as sort_order,
6537+
(item->>'status_id')::uuid as status_id,
6538+
(item->>'priority_id')::uuid as priority_id,
6539+
(item->>'phase_id')::uuid as phase_id
6540+
FROM json_array_elements(_updates) as item
6541+
LOOP
6542+
UPDATE tasks
6543+
SET
6544+
sort_order = _update_record.sort_order,
6545+
status_id = COALESCE(_update_record.status_id, status_id),
6546+
priority_id = COALESCE(_update_record.priority_id, priority_id)
6547+
WHERE id = _update_record.task_id;
6548+
6549+
-- Handle phase updates separately since it's in a different table
6550+
IF _update_record.phase_id IS NOT NULL THEN
6551+
INSERT INTO task_phase (task_id, phase_id)
6552+
VALUES (_update_record.task_id, _update_record.phase_id)
6553+
ON CONFLICT (task_id) DO UPDATE SET phase_id = _update_record.phase_id;
6554+
END IF;
6555+
END LOOP;
6556+
END
6557+
$$;

worklenz-backend/src/controllers/task-phases-controller.ts

Lines changed: 7 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -16,19 +16,23 @@ export default class TaskPhasesController extends WorklenzControllerBase {
1616
if (!req.query.id)
1717
return res.status(400).send(new ServerResponse(false, null, "Invalid request"));
1818

19+
// Use custom name if provided, otherwise use default naming pattern
20+
const phaseName = req.body.name?.trim() ||
21+
`Untitled Phase (${(await db.query("SELECT COUNT(*) FROM project_phases WHERE project_id = $1", [req.query.id])).rows[0].count + 1})`;
22+
1923
const q = `
2024
INSERT INTO project_phases (name, color_code, project_id, sort_index)
2125
VALUES (
22-
CONCAT('Untitled Phase (', (SELECT COUNT(*) FROM project_phases WHERE project_id = $2) + 1, ')'),
2326
$1,
2427
$2,
25-
(SELECT COUNT(*) FROM project_phases WHERE project_id = $2) + 1)
28+
$3,
29+
(SELECT COUNT(*) FROM project_phases WHERE project_id = $3) + 1)
2630
RETURNING id, name, color_code, sort_index;
2731
`;
2832

2933
req.body.color_code = this.DEFAULT_PHASE_COLOR;
3034

31-
const result = await db.query(q, [req.body.color_code, req.query.id]);
35+
const result = await db.query(q, [phaseName, req.body.color_code, req.query.id]);
3236
const [data] = result.rows;
3337

3438
data.color_code = getColor(data.name) + TASK_STATUS_COLOR_ALPHA;

0 commit comments

Comments
 (0)