Skip to content

Commit b5c69ba

Browse files
Merge pull request liam-hq#1435 from liam-hq/modify-github-pull-request-table
♻️(database): Refactor GitHub PR comments into separate table
2 parents f20e860 + 2a2f2a7 commit b5c69ba

File tree

8 files changed

+212
-25
lines changed

8 files changed

+212
-25
lines changed

.liam/schema-override.yml

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -7,6 +7,7 @@ overrides:
77
- github_pull_requests
88
- github_schema_file_paths
99
- github_doc_file_paths
10+
- github_pull_request_comments
1011
comment: "Tables related to GitHub. All tables in this group should have a GitHub prefix. External tables must not depend on these tables (e.g., no pullRequestId foreign keys in tables outside this group)."
1112
Organization:
1213
name: Organization
@@ -21,6 +22,8 @@ overrides:
2122
comment: "GitHub repositories table with github_ prefix pattern"
2223
github_pull_requests:
2324
comment: "GitHub pull requests table with github_ prefix pattern. Should be updated to have migration_id instead of migrations having pull_request_id."
25+
github_pull_request_comments:
26+
comment: "Stores GitHub comment information for pull requests, maintaining a 1:1 relationship with github_pull_requests"
2427
migrations:
2528
comment: "Should have project_id instead of pull_request_id to remove GitHub dependency"
2629
overall_reviews:

docs/migrationOpsContext.md

Lines changed: 46 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -63,5 +63,51 @@ Write Postgres-compatible SQL code for Supabase migration files that:
6363
- Write all SQL in lowercase.
6464
- Add copious comments for any destructive SQL commands, including truncating, dropping, or column alterations.
6565
- **Follow the schema design patterns and rules documented in [`docs/schemaPatterns.md`](./schemaPatterns.md)** for consistent database design.
66+
- **Wrap migrations in a transaction (BEGIN/COMMIT) to ensure atomicity**. This prevents partial migrations if an error occurs.
6667

6768
The generated SQL code should be production-ready, well-documented, and aligned with Supabase's best practices.
69+
70+
## Adding NOT NULL Constraints
71+
72+
When adding a NOT NULL constraint to an existing column:
73+
74+
1. Always update existing rows with appropriate values before applying the constraint.
75+
2. For columns with foreign key references, derive values from related tables when possible.
76+
3. Example approach:
77+
78+
```sql
79+
BEGIN;
80+
81+
-- Add the column as nullable first
82+
ALTER TABLE "public"."table_name"
83+
ADD COLUMN "new_column" uuid REFERENCES "public"."referenced_table"("id") ON UPDATE CASCADE ON DELETE RESTRICT;
84+
85+
-- Update existing rows with values from a related table
86+
UPDATE "public"."table_name" tn
87+
SET "new_column" = (
88+
SELECT rt."id"
89+
FROM "public"."referenced_table" rt
90+
JOIN "public"."join_table" jt ON rt."id" = jt."referenced_id"
91+
WHERE jt."table_id" = tn."id"
92+
LIMIT 1
93+
);
94+
95+
-- Now make the column NOT NULL
96+
ALTER TABLE "public"."table_name"
97+
ALTER COLUMN "new_column" SET NOT NULL;
98+
99+
COMMIT;
100+
```
101+
102+
## Post-Migration Steps
103+
104+
After applying migrations, always run:
105+
106+
1. Run the combined command to update both schema SQL file and TypeScript types:
107+
```sh
108+
cd frontend/packages/db && pnpm supabase:gen
109+
```
110+
111+
2. Test affected functionality to ensure backward compatibility with the previous app version.
112+
113+
3. Update all test files that might be affected by schema changes, especially where strict typing is enforced.

frontend/packages/db/schema/schema.sql

Lines changed: 38 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -163,10 +163,21 @@ CREATE TABLE IF NOT EXISTS "public"."github_doc_file_paths" (
163163
ALTER TABLE "public"."github_doc_file_paths" OWNER TO "postgres";
164164

165165

166+
CREATE TABLE IF NOT EXISTS "public"."github_pull_request_comments" (
167+
"id" "uuid" DEFAULT "gen_random_uuid"() NOT NULL,
168+
"github_pull_request_id" "uuid" NOT NULL,
169+
"github_comment_identifier" integer NOT NULL,
170+
"created_at" timestamp(3) with time zone DEFAULT CURRENT_TIMESTAMP NOT NULL,
171+
"updated_at" timestamp(3) with time zone NOT NULL
172+
);
173+
174+
175+
ALTER TABLE "public"."github_pull_request_comments" OWNER TO "postgres";
176+
177+
166178
CREATE TABLE IF NOT EXISTS "public"."github_pull_requests" (
167179
"id" "uuid" DEFAULT "gen_random_uuid"() NOT NULL,
168180
"pull_number" bigint NOT NULL,
169-
"comment_id" bigint,
170181
"created_at" timestamp(3) with time zone DEFAULT CURRENT_TIMESTAMP NOT NULL,
171182
"updated_at" timestamp(3) with time zone NOT NULL,
172183
"repository_id" "uuid" NOT NULL
@@ -402,6 +413,21 @@ ALTER TABLE ONLY "public"."github_doc_file_paths"
402413

403414

404415

416+
ALTER TABLE ONLY "public"."github_pull_request_comments"
417+
ADD CONSTRAINT "github_pull_request_comments_github_comment_identifier_key" UNIQUE ("github_comment_identifier");
418+
419+
420+
421+
ALTER TABLE ONLY "public"."github_pull_request_comments"
422+
ADD CONSTRAINT "github_pull_request_comments_github_pull_request_id_key" UNIQUE ("github_pull_request_id");
423+
424+
425+
426+
ALTER TABLE ONLY "public"."github_pull_request_comments"
427+
ADD CONSTRAINT "github_pull_request_comments_pkey" PRIMARY KEY ("id");
428+
429+
430+
405431
ALTER TABLE ONLY "public"."github_repositories"
406432
ADD CONSTRAINT "github_repository_github_repository_identifier_organization_id_" UNIQUE ("github_repository_identifier", "organization_id");
407433

@@ -568,6 +594,11 @@ ALTER TABLE ONLY "public"."github_doc_file_paths"
568594

569595

570596

597+
ALTER TABLE ONLY "public"."github_pull_request_comments"
598+
ADD CONSTRAINT "github_pull_request_comments_github_pull_request_id_fkey" FOREIGN KEY ("github_pull_request_id") REFERENCES "public"."github_pull_requests"("id") ON UPDATE CASCADE ON DELETE CASCADE;
599+
600+
601+
571602
ALTER TABLE ONLY "public"."github_pull_requests"
572603
ADD CONSTRAINT "github_pull_request_repository_id_fkey" FOREIGN KEY ("repository_id") REFERENCES "public"."github_repositories"("id") ON UPDATE CASCADE ON DELETE RESTRICT;
573604

@@ -987,6 +1018,12 @@ GRANT ALL ON TABLE "public"."github_doc_file_paths" TO "service_role";
9871018

9881019

9891020

1021+
GRANT ALL ON TABLE "public"."github_pull_request_comments" TO "anon";
1022+
GRANT ALL ON TABLE "public"."github_pull_request_comments" TO "authenticated";
1023+
GRANT ALL ON TABLE "public"."github_pull_request_comments" TO "service_role";
1024+
1025+
1026+
9901027
GRANT ALL ON TABLE "public"."github_pull_requests" TO "anon";
9911028
GRANT ALL ON TABLE "public"."github_pull_requests" TO "authenticated";
9921029
GRANT ALL ON TABLE "public"."github_pull_requests" TO "service_role";

frontend/packages/db/supabase/database.types.ts

Lines changed: 32 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -69,25 +69,54 @@ export type Database = {
6969
},
7070
]
7171
}
72+
github_pull_request_comments: {
73+
Row: {
74+
created_at: string
75+
github_comment_identifier: number
76+
github_pull_request_id: string
77+
id: string
78+
updated_at: string
79+
}
80+
Insert: {
81+
created_at?: string
82+
github_comment_identifier: number
83+
github_pull_request_id: string
84+
id?: string
85+
updated_at: string
86+
}
87+
Update: {
88+
created_at?: string
89+
github_comment_identifier?: number
90+
github_pull_request_id?: string
91+
id?: string
92+
updated_at?: string
93+
}
94+
Relationships: [
95+
{
96+
foreignKeyName: 'github_pull_request_comments_github_pull_request_id_fkey'
97+
columns: ['github_pull_request_id']
98+
isOneToOne: true
99+
referencedRelation: 'github_pull_requests'
100+
referencedColumns: ['id']
101+
},
102+
]
103+
}
72104
github_pull_requests: {
73105
Row: {
74-
comment_id: number | null
75106
created_at: string
76107
id: string
77108
pull_number: number
78109
repository_id: string
79110
updated_at: string
80111
}
81112
Insert: {
82-
comment_id?: number | null
83113
created_at?: string
84114
id?: string
85115
pull_number: number
86116
repository_id: string
87117
updated_at: string
88118
}
89119
Update: {
90-
comment_id?: number | null
91120
created_at?: string
92121
id?: string
93122
pull_number?: number
Lines changed: 62 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,62 @@
1+
/*
2+
Migration: Refactor github_pull_requests table and create github_pull_request_comments table
3+
4+
Purpose:
5+
- Remove comment_id from github_pull_requests to avoid record updates, moving it to a separate table
6+
- Create new github_pull_request_comments table with a 1:1 relationship to github_pull_requests
7+
8+
Affected tables:
9+
- github_pull_requests
10+
- github_pull_request_comments (new)
11+
*/
12+
13+
BEGIN;
14+
15+
-- Step 1: Create the new github_pull_request_comments table
16+
CREATE TABLE "public"."github_pull_request_comments" (
17+
"id" uuid DEFAULT gen_random_uuid() NOT NULL,
18+
"github_pull_request_id" uuid NOT NULL,
19+
"github_comment_identifier" integer NOT NULL,
20+
"created_at" timestamp(3) with time zone DEFAULT CURRENT_TIMESTAMP NOT NULL,
21+
"updated_at" timestamp(3) with time zone NOT NULL,
22+
23+
-- Define primary key
24+
CONSTRAINT "github_pull_request_comments_pkey" PRIMARY KEY ("id"),
25+
26+
-- Define unique constraint on github_pull_request_id for 1:1 relationship
27+
CONSTRAINT "github_pull_request_comments_github_pull_request_id_key" UNIQUE ("github_pull_request_id"),
28+
29+
-- Define unique constraint on github_comment_identifier
30+
CONSTRAINT "github_pull_request_comments_github_comment_identifier_key" UNIQUE ("github_comment_identifier"),
31+
32+
-- Define foreign key relationship to github_pull_requests
33+
CONSTRAINT "github_pull_request_comments_github_pull_request_id_fkey"
34+
FOREIGN KEY ("github_pull_request_id")
35+
REFERENCES "public"."github_pull_requests"("id")
36+
ON UPDATE CASCADE
37+
ON DELETE CASCADE
38+
);
39+
40+
-- Grant permissions on the new table
41+
GRANT ALL ON TABLE "public"."github_pull_request_comments" TO "anon";
42+
GRANT ALL ON TABLE "public"."github_pull_request_comments" TO "authenticated";
43+
GRANT ALL ON TABLE "public"."github_pull_request_comments" TO "service_role";
44+
45+
-- Step 2: Optionally migrate existing comment_id data to the new table
46+
-- This will preserve any existing comment relationships
47+
INSERT INTO "public"."github_pull_request_comments"
48+
("github_pull_request_id", "github_comment_identifier", "updated_at")
49+
SELECT
50+
"id",
51+
"comment_id",
52+
CURRENT_TIMESTAMP
53+
FROM "public"."github_pull_requests"
54+
WHERE "comment_id" IS NOT NULL;
55+
56+
-- Step 3: Finally, drop the comment_id column from github_pull_requests
57+
-- This comment explains the potentially destructive operation
58+
-- Note: This is a destructive operation, but we've migrated the data in step 2
59+
ALTER TABLE "public"."github_pull_requests"
60+
DROP COLUMN "comment_id";
61+
62+
COMMIT;

frontend/packages/jobs/src/tasks/review/__tests__/postComment.test.ts

Lines changed: 12 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -44,7 +44,6 @@ describe.skip('postComment', () => {
4444
id: '9999',
4545
pull_number: 1,
4646
repository_id: '9999',
47-
comment_id: null,
4847
created_at: new Date().toISOString(),
4948
updated_at: new Date().toISOString(),
5049
}
@@ -110,21 +109,23 @@ Migration URL: ${process.env['NEXT_PUBLIC_BASE_URL']}/app/migrations/${testMigra
110109
)
111110
expect(createPullRequestComment).toHaveBeenCalledTimes(1)
112111

113-
const { data: updatedPR } = await supabase
114-
.from('github_pull_requests')
115-
.select('*')
116-
.eq('id', testPullRequest.id)
117-
.single()
112+
const { data: prComment } = await supabase
113+
.from('github_pull_request_comments')
114+
.select('github_comment_identifier')
115+
.eq('github_pull_request_id', testPullRequest.id)
116+
.maybeSingle()
118117

119-
expect(updatedPR?.comment_id).toBe(mockCommentId)
118+
expect(prComment?.github_comment_identifier).toBe(mockCommentId)
120119
})
121120

122121
it('should update existing comment when comment exists', async () => {
123122
const existingCommentId = 456
124-
await supabase
125-
.from('github_pull_requests')
126-
.update({ comment_id: existingCommentId })
127-
.eq('id', testPullRequest.id)
123+
const now = new Date().toISOString()
124+
await supabase.from('github_pull_request_comments').insert({
125+
github_pull_request_id: testPullRequest.id,
126+
github_comment_identifier: existingCommentId,
127+
updated_at: now,
128+
})
128129

129130
const testPayload = {
130131
reviewComment: 'Updated review comment',
@@ -183,7 +184,6 @@ Migration URL: ${process.env['NEXT_PUBLIC_BASE_URL']}/app/migrations/${testMigra
183184
id: '8888',
184185
pull_number: 2,
185186
repository_id: testRepository.id,
186-
comment_id: null,
187187
created_at: new Date().toISOString(),
188188
updated_at: new Date().toISOString(),
189189
}

frontend/packages/jobs/src/tasks/review/__tests__/processSaveReview.test.ts

Lines changed: 0 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -28,7 +28,6 @@ describe.skip('processSaveReview', () => {
2828
id: '9999',
2929
pull_number: 9999,
3030
repository_id: '9999',
31-
comment_id: null,
3231
created_at: new Date().toISOString(),
3332
updated_at: new Date().toISOString(),
3433
}

frontend/packages/jobs/src/tasks/review/postComment.ts

Lines changed: 19 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -118,6 +118,13 @@ export async function postComment(
118118
)
119119
}
120120

121+
// Fetch comment ID from github_pull_request_comments if exists
122+
const { data: commentRecord } = await supabase
123+
.from('github_pull_request_comments')
124+
.select('github_comment_identifier')
125+
.eq('github_pull_request_id', pullRequestId)
126+
.maybeSingle()
127+
121128
const migration = prRecord.migrations[0]
122129
const migrationUrl = `${process.env['NEXT_PUBLIC_BASE_URL']}/app/projects/${projectId}/ref/${encodeURIComponent(branchName)}/migrations/${migration.id}`
123130

@@ -139,12 +146,12 @@ export async function postComment(
139146

140147
const fullComment = `${reviewComment}\n\nMigration URL: ${migrationUrl}${erdLinkText}`
141148

142-
if (prRecord.comment_id) {
149+
if (commentRecord?.github_comment_identifier) {
143150
await updatePullRequestComment(
144151
Number(installationId),
145152
owner,
146153
repo,
147-
Number(prRecord.comment_id),
154+
Number(commentRecord.github_comment_identifier),
148155
fullComment,
149156
)
150157
} else {
@@ -156,14 +163,18 @@ export async function postComment(
156163
fullComment,
157164
)
158165

159-
const { error: updateError } = await supabase
160-
.from('github_pull_requests')
161-
.update({ comment_id: commentResponse.id })
162-
.eq('id', pullRequestId)
166+
const now = new Date().toISOString()
167+
const { error: createCommentError } = await supabase
168+
.from('github_pull_request_comments')
169+
.insert({
170+
github_pull_request_id: pullRequestId,
171+
github_comment_identifier: commentResponse.id,
172+
updated_at: now,
173+
})
163174

164-
if (updateError) {
175+
if (createCommentError) {
165176
throw new Error(
166-
`Failed to update pull request with comment ID: ${updateError.message}`,
177+
`Failed to create github_pull_request_comments record: ${createCommentError.message}`,
167178
)
168179
}
169180
}

0 commit comments

Comments
 (0)