@@ -7,7 +7,17 @@ import {
77 workspaceFileVersion ,
88} from '@sim/db/schema'
99import { generateId } from '@sim/utils/id'
10- import { and , desc , eq , inArray , isNotNull , lt , notInArray , sql } from 'drizzle-orm'
10+ import {
11+ and ,
12+ desc ,
13+ eq ,
14+ inArray ,
15+ isNotNull ,
16+ lt ,
17+ notInArray ,
18+ type SQLWrapper ,
19+ sql ,
20+ } from 'drizzle-orm'
1121import {
1222 type CursorKey ,
1323 type KeysetKey ,
@@ -105,14 +115,14 @@ export async function loadWorkspaceFileVersionHead(
105115
106116/**
107117 * Batched {@link loadWorkspaceFileVersionHead}: each file's highest version, keyed by file id.
108- * The max is resolved from the `(file_id, version)` index first, so only head rows are fetched
109- * rather than every retained version.
118+ * `fileIds` is a subquery, so the file set never becomes a bind-parameter list. The max is
119+ * resolved from the `(file_id, version)` index first, so only head rows are fetched rather than
120+ * every retained version.
110121 */
111122export async function loadWorkspaceFileVersionHeads (
112- fileIds : string [ ] ,
123+ fileIds : SQLWrapper ,
113124 executor : DbOrTx = db
114125) : Promise < Map < string , WorkspaceFileVersionSummaryRow > > {
115- if ( ! fileIds . length ) return new Map ( )
116126 const heads = await executor
117127 . select ( versionSummaryColumns )
118128 . from ( workspaceFileVersion )
@@ -713,27 +723,36 @@ export function getCurrentWorkspaceFileVersion(
713723 return withVersionSnapshot ( file , currentVersionInSnapshot , executor )
714724}
715725
726+ /**
727+ * Correlated columns for subqueries over the enclosing query's `workspace_files` row. Both sides
728+ * are table-qualified because Drizzle renders single-table columns bare, which would bind the outer
729+ * columns to the subquery's own table.
730+ */
731+ function correlatedVersionColumns ( ) {
732+ const qualified = ( table : typeof workspaceFileVersion | typeof workspaceFiles , name : string ) =>
733+ sql `${ table } .${ sql . identifier ( name ) } `
734+ return {
735+ head : {
736+ fileId : qualified ( workspaceFileVersion , workspaceFileVersion . fileId . name ) ,
737+ version : qualified ( workspaceFileVersion , workspaceFileVersion . version . name ) ,
738+ key : qualified ( workspaceFileVersion , workspaceFileVersion . key . name ) ,
739+ supersededAt : qualified ( workspaceFileVersion , workspaceFileVersion . supersededAt . name ) ,
740+ } ,
741+ file : {
742+ id : qualified ( workspaceFiles , workspaceFiles . id . name ) ,
743+ key : qualified ( workspaceFiles , workspaceFiles . key . name ) ,
744+ } ,
745+ }
746+ }
747+
716748/**
717749 * The current version number of the enclosing query's `workspace_files` row, as a correlated
718750 * subquery so the row and its number come from one statement's snapshot. The newest row numbers
719751 * the file's bytes while it still holds them; otherwise the bytes are on the implicit version after
720752 * it, and a file with no rows is on version 1 — the numbering {@link implicitCurrentVersion} gives.
721- * Both sides of the correlation are table-qualified because Drizzle renders single-table columns
722- * bare, which would bind the outer columns to this subquery's own table.
723753 */
724754export function currentWorkspaceFileVersionNumberSql ( ) {
725- const qualified = ( table : typeof workspaceFileVersion | typeof workspaceFiles , name : string ) =>
726- sql `${ table } .${ sql . identifier ( name ) } `
727- const head = {
728- fileId : qualified ( workspaceFileVersion , workspaceFileVersion . fileId . name ) ,
729- version : qualified ( workspaceFileVersion , workspaceFileVersion . version . name ) ,
730- key : qualified ( workspaceFileVersion , workspaceFileVersion . key . name ) ,
731- supersededAt : qualified ( workspaceFileVersion , workspaceFileVersion . supersededAt . name ) ,
732- }
733- const file = {
734- id : qualified ( workspaceFiles , workspaceFiles . id . name ) ,
735- key : qualified ( workspaceFiles , workspaceFiles . key . name ) ,
736- }
755+ const { head, file } = correlatedVersionColumns ( )
737756 const number = sql `case when ${ head . supersededAt } is null and ${ head . key } = ${ file . key } then ${ head . version } else ${ head . version } + 1 end`
738757 return sql < number > `coalesce((
739758 select ${ number } from ${ workspaceFileVersion }
@@ -743,6 +762,17 @@ export function currentWorkspaceFileVersionNumberSql() {
743762 ), ${ INITIAL_WORKSPACE_FILE_VERSION } )` . mapWith ( Number )
744763}
745764
765+ /** {@link isVersionHeadCurrent } for the enclosing query's `workspace_files` row. */
766+ export function workspaceFileVersionHeadCurrentSql ( ) {
767+ const { head, file } = correlatedVersionColumns ( )
768+ return sql < boolean > `coalesce((
769+ select ${ head . supersededAt } is null and ${ head . key } = ${ file . key } from ${ workspaceFileVersion }
770+ where ${ head . fileId } = ${ file . id }
771+ order by ${ head . version } desc
772+ limit 1
773+ ), false)`
774+ }
775+
746776/** One version of a file, or null when it never existed or retention removed it. */
747777export function getWorkspaceFileVersion (
748778 file : WorkspaceFileVersionSubject ,
0 commit comments