@@ -96,10 +96,8 @@ export async function listPackagesForApi(
9696 }
9797
9898 if ( opts . busFactor1Only ) {
99- // UNIQUE(package_id, maintainer_id) on package_maintainers covers this lookup
100- conditions . push (
101- `(SELECT COUNT(*) FROM package_maintainers pm WHERE pm.package_id = p.id) = 1` ,
102- )
99+ // References pm_counts LATERAL join below — computed once, used in both WHERE and SELECT
100+ conditions . push ( `pm_counts.cnt = 1` )
103101 }
104102
105103 const where = `WHERE ${ conditions . join ( ' AND ' ) } `
@@ -111,9 +109,8 @@ export async function listPackagesForApi(
111109 else sortExpr = 'LOWER(p.name)'
112110 const sortDir = opts . sortDir === 'desc' ? 'DESC' : 'ASC'
113111
114- const filterParams = { ...params }
115- params . limit = opts . pageSize
116- params . offset = ( opts . page - 1 ) * opts . pageSize
112+ // Separate paginated params from filter-only params used by the fallback COUNT query
113+ const queryParams = { ...params , limit : opts . pageSize , offset : ( opts . page - 1 ) * opts . pageSize }
117114
118115 const rows : PackageListRow [ ] = await qx . select (
119116 `
@@ -124,18 +121,21 @@ export async function listPackagesForApi(
124121 p.impact AS "criticalityScore",
125122 s.status AS "stewardshipStatus",
126123 COALESCE(ap_counts.cnt, 0) AS "openVulns",
127- (SELECT COUNT(*)::int FROM package_maintainers pm WHERE pm.package_id = p.id) AS "maintainerCount",
124+ pm_counts.cnt AS "maintainerCount",
128125 COUNT(*) OVER() AS total
129126 FROM packages p
130127 LEFT JOIN stewardships s ON s.package_id = p.id
131128 LEFT JOIN LATERAL (
132129 SELECT COUNT(*)::int AS cnt FROM advisory_packages WHERE package_id = p.id
133130 ) ap_counts ON true
131+ LEFT JOIN LATERAL (
132+ SELECT COUNT(*)::int AS cnt FROM package_maintainers pm WHERE pm.package_id = p.id
133+ ) pm_counts ON true
134134 ${ where }
135135 ORDER BY ${ sortExpr } ${ sortDir } NULLS LAST, p.purl ${ sortDir }
136136 LIMIT $(limit) OFFSET $(offset)
137137 ` ,
138- params ,
138+ queryParams ,
139139 )
140140
141141 let total : number
@@ -145,8 +145,14 @@ export async function listPackagesForApi(
145145 // Window function returns no rows when the page is beyond the result set.
146146 // Fall back to a separate COUNT so the caller always gets the real total.
147147 const countRow : { count : string } = await qx . selectOne (
148- `SELECT COUNT(*)::text AS count FROM packages p LEFT JOIN stewardships s ON s.package_id = p.id ${ where } ` ,
149- filterParams ,
148+ `SELECT COUNT(*)::text AS count
149+ FROM packages p
150+ LEFT JOIN stewardships s ON s.package_id = p.id
151+ LEFT JOIN LATERAL (
152+ SELECT COUNT(*)::int AS cnt FROM package_maintainers pm WHERE pm.package_id = p.id
153+ ) pm_counts ON true
154+ ${ where } ` ,
155+ params ,
150156 )
151157 total = parseInt ( countRow . count , 10 )
152158 }
0 commit comments