MySQL · 3064 bytes Raw Blame History
1 -- SPDX-License-Identifier: AGPL-3.0-or-later
2 --
3 -- Repos table:
4 --
5 -- - One row per repository. Owner is XOR (user OR org); orgs land in
6 -- S31 but the column exists from day 1 so the FK isn't a future
7 -- destructive migration.
8 -- - Name is citext so case-insensitive uniqueness comes for free.
9 -- - default_branch is "trunk" by default; we'll let owners change it
10 -- later. The bare repo on disk is created with the same default
11 -- (--initial-branch=trunk via storage.RepoFS.InitBare).
12 -- - Soft-delete + archive flags are introduced now so callers don't
13 -- have to retrofit them later.
14 -- - disk_used_bytes is the recorded post-init size; recalc job is S14.
15 -- - has_issues / has_pulls let owners turn off these surfaces. Default
16 -- on; the per-repo settings UI lands later.
17
18 -- +goose Up
19 CREATE TYPE repo_visibility AS ENUM ('public', 'private');
20
21 CREATE TABLE repos (
22 id bigserial PRIMARY KEY,
23 owner_user_id bigint REFERENCES users(id) ON DELETE CASCADE,
24 owner_org_id bigint, -- FK added when orgs ship in S31
25 name citext NOT NULL,
26 description text NOT NULL DEFAULT '',
27 visibility repo_visibility NOT NULL DEFAULT 'public',
28 default_branch text NOT NULL DEFAULT 'trunk',
29 is_archived boolean NOT NULL DEFAULT false,
30 archived_at timestamptz,
31 deleted_at timestamptz,
32 disk_used_bytes bigint NOT NULL DEFAULT 0,
33 fork_of_repo_id bigint REFERENCES repos(id) ON DELETE SET NULL,
34 license_key text,
35 primary_language text,
36 has_issues boolean NOT NULL DEFAULT true,
37 has_pulls boolean NOT NULL DEFAULT true,
38 created_at timestamptz NOT NULL DEFAULT now(),
39 updated_at timestamptz NOT NULL DEFAULT now(),
40
41 -- Exactly one owner kind. Until S31 ships orgs the right side is
42 -- always NULL; the constraint shape is forward-compatible.
43 CONSTRAINT repos_owner_xor CHECK (
44 (owner_user_id IS NOT NULL AND owner_org_id IS NULL)
45 OR (owner_user_id IS NULL AND owner_org_id IS NOT NULL)
46 ),
47 CONSTRAINT repos_name_length CHECK (char_length(name::text) BETWEEN 1 AND 100),
48 CONSTRAINT repos_description_length CHECK (char_length(description) <= 350),
49 CONSTRAINT repos_default_branch_length CHECK (char_length(default_branch) BETWEEN 1 AND 100)
50 );
51
52 -- Per-owner uniqueness. Two partial indexes — one per owner kind —
53 -- so a name can't collide within either bucket.
54 CREATE UNIQUE INDEX repos_owner_user_name_idx
55 ON repos (owner_user_id, name)
56 WHERE owner_user_id IS NOT NULL;
57
58 CREATE UNIQUE INDEX repos_owner_org_name_idx
59 ON repos (owner_org_id, name)
60 WHERE owner_org_id IS NOT NULL;
61
62 CREATE INDEX repos_visibility_idx ON repos (visibility);
63 CREATE INDEX repos_deleted_at_idx ON repos (deleted_at) WHERE deleted_at IS NOT NULL;
64
65 CREATE TRIGGER set_updated_at BEFORE UPDATE ON repos
66 FOR EACH ROW EXECUTE FUNCTION tg_set_updated_at();
67
68 -- +goose Down
69 DROP TABLE IF EXISTS repos;
70 DROP TYPE IF EXISTS repo_visibility;
71