MySQL · 3115 bytes Raw Blame History
1 -- SPDX-License-Identifier: AGPL-3.0-or-later
2 --
3 -- S27 fork-support columns + the maintenance trigger.
4 --
5 -- The S11 / S27 specs assumed `is_fork` and `fork_count` were already
6 -- in `repos`; in fact only `fork_of_repo_id` shipped. We add
7 -- `fork_count` here. `is_fork` is intentionally skipped — it would
8 -- duplicate the truth of `fork_of_repo_id IS NOT NULL`. Same gap
9 -- pattern as S26 caught for `star_count`/`watcher_count`; noted in
10 -- the S27 status block.
11 --
12 -- `init_status` tracks the async clone job's progression. Synchronous
13 -- repo creates (the S11 path) write 'initialized' directly. Forks
14 -- start at 'init_pending' and the worker flips to 'initialized' on
15 -- success or 'init_failed' on permanent failure (poison error). The
16 -- repo home view reads this column to decide between "your fork is
17 -- being prepared" placeholder and the real tree view.
18
19 -- +goose Up
20 ALTER TABLE repos
21 ADD COLUMN fork_count bigint NOT NULL DEFAULT 0;
22
23 CREATE TYPE repo_init_status AS ENUM ('initialized', 'init_pending', 'init_failed');
24
25 -- Default 'initialized' so the back-fill on existing rows is correct
26 -- (every pre-S27 repo was created synchronously).
27 ALTER TABLE repos
28 ADD COLUMN init_status repo_init_status NOT NULL DEFAULT 'initialized';
29
30 -- Maintenance trigger: when a repos row with fork_of_repo_id IS NOT NULL
31 -- is inserted (or hard-deleted), bump the source repo's fork_count.
32 -- Soft delete (deleted_at IS NOT NULL) does NOT decrement — the row
33 -- is still present, hard-delete is what cascades. ON DELETE SET NULL
34 -- on `fork_of_repo_id` would NULL the column on source delete; the
35 -- delete trigger fires before that on the *fork* row.
36
37 -- +goose StatementBegin
38 CREATE OR REPLACE FUNCTION tg_forks_count_inc() RETURNS trigger
39 LANGUAGE plpgsql AS $$
40 BEGIN
41 IF NEW.fork_of_repo_id IS NOT NULL THEN
42 UPDATE repos SET fork_count = fork_count + 1
43 WHERE id = NEW.fork_of_repo_id;
44 END IF;
45 RETURN NEW;
46 END;
47 $$;
48 -- +goose StatementEnd
49
50 -- +goose StatementBegin
51 CREATE OR REPLACE FUNCTION tg_forks_count_dec() RETURNS trigger
52 LANGUAGE plpgsql AS $$
53 BEGIN
54 IF OLD.fork_of_repo_id IS NOT NULL THEN
55 UPDATE repos SET fork_count = GREATEST(fork_count - 1, 0)
56 WHERE id = OLD.fork_of_repo_id;
57 END IF;
58 RETURN OLD;
59 END;
60 $$;
61 -- +goose StatementEnd
62
63 CREATE TRIGGER forks_count_inc AFTER INSERT ON repos
64 FOR EACH ROW EXECUTE FUNCTION tg_forks_count_inc();
65
66 CREATE TRIGGER forks_count_dec AFTER DELETE ON repos
67 FOR EACH ROW EXECUTE FUNCTION tg_forks_count_dec();
68
69 -- Listing forks of a repo: index on the FK + recency.
70 CREATE INDEX repos_fork_of_repo_id_idx
71 ON repos (fork_of_repo_id, created_at DESC)
72 WHERE fork_of_repo_id IS NOT NULL;
73
74 -- +goose Down
75 DROP INDEX IF EXISTS repos_fork_of_repo_id_idx;
76 DROP TRIGGER IF EXISTS forks_count_dec ON repos;
77 DROP TRIGGER IF EXISTS forks_count_inc ON repos;
78 DROP FUNCTION IF EXISTS tg_forks_count_dec();
79 DROP FUNCTION IF EXISTS tg_forks_count_inc();
80 ALTER TABLE repos
81 DROP COLUMN IF EXISTS init_status,
82 DROP COLUMN IF EXISTS fork_count;
83 DROP TYPE IF EXISTS repo_init_status;
84