@@ -193,6 +193,35 @@ describe("database persistence helpers", () => {
193193 const cappedPathSet = new Set ( cappedPaths . map ( ( entry ) => entry . path ) ) ;
194194 expect ( graph . paths . every ( ( entry ) => cappedPathSet . has ( entry . path ) ) ) . toBe ( true ) ;
195195 } ) ;
196+
197+ it ( "REGRESSION: upsertPullRequestFile targets the id PRIMARY KEY in its ON CONFLICT clause, not the secondary unique index" , async ( ) => {
198+ // `id` is a pure function of (repoFullName, pullNumber, path) — the exact same fields the secondary
199+ // unique index covers — so targeting that index instead of `id` leaves the primary key unprotected by
200+ // Postgres's upsert machinery on the self-host Postgres backend (see #977's pg-adapter): a genuinely
201+ // concurrent second writer can still raise a raw duplicate-key error on `pull_request_files_pkey` even
202+ // though the composite fields "agree." Asserting the generated SQL's conflict target pins the fix so a
203+ // future revert back to the composite target doesn't silently reopen the race.
204+ const env = createTestEnv ( ) ;
205+ const realPrepare = env . DB . prepare . bind ( env . DB ) ;
206+ const conflictClauses : string [ ] = [ ] ;
207+ env . DB . prepare = ( ( sql : string ) => {
208+ if ( / i n s e r t \s + i n t o \s + [ " ' ` ] ? p u l l _ r e q u e s t _ f i l e s / i. test ( sql ) ) {
209+ const match = / o n \s + c o n f l i c t \s * \( ( [ ^ ) ] * ) \) / i. exec ( sql ) ;
210+ if ( match ) conflictClauses . push ( match [ 1 ] ! . trim ( ) ) ;
211+ }
212+ return realPrepare ( sql ) ;
213+ } ) as typeof env . DB . prepare ;
214+
215+ await upsertPullRequestFile ( env , pullRequestFile ( "owner/repo" , 1 , "src/a.ts" ) ) ;
216+
217+ expect ( conflictClauses ) . toHaveLength ( 1 ) ;
218+ expect ( conflictClauses [ 0 ] ) . toMatch ( / ^ [ " ' ` ] ? ( p u l l _ r e q u e s t _ f i l e s [ " ' ` ] ? \. [ " ' ` ] ? ) ? i d [ " ' ` ] ? $ / i) ;
219+
220+ // Functional guard: a same-key upsert still updates the existing row in place rather than duplicating it.
221+ await upsertPullRequestFile ( env , { ...pullRequestFile ( "owner/repo" , 1 , "src/a.ts" ) , additions : 99 } ) ;
222+ const rows = await listRepoPullRequestFilePaths ( env , "owner/repo" , { pullNumbers : [ 1 ] } ) ;
223+ expect ( rows ) . toHaveLength ( 1 ) ;
224+ } ) ;
196225} ) ;
197226
198227function pullRequestFile ( repoFullName : string , pullNumber : number , path : string ) : PullRequestFileRecord {
0 commit comments