// Code generated by sqlc. DO NOT EDIT. // versions: // sqlc v1.31.1 // source: workspace.sql package db import ( "context" "github.com/jackc/pgx/v5/pgtype" ) const createWorkspace = `-- name: CreateWorkspace :one INSERT INTO workspace (name, slug, description, context, issue_prefix) VALUES ($1, $2, $3, $4, $5) RETURNING id, name, slug, description, settings, created_at, updated_at, context, repos, issue_prefix, issue_counter, avatar_url, attribution_fail_closed ` type CreateWorkspaceParams struct { Name string `json:"name"` Slug string `json:"slug"` Description pgtype.Text `json:"description"` Context pgtype.Text `json:"context"` IssuePrefix string `json:"issue_prefix"` } func (q *Queries) CreateWorkspace(ctx context.Context, arg CreateWorkspaceParams) (Workspace, error) { row := q.db.QueryRow(ctx, createWorkspace, arg.Name, arg.Slug, arg.Description, arg.Context, arg.IssuePrefix, ) var i Workspace err := row.Scan( &i.ID, &i.Name, &i.Slug, &i.Description, &i.Settings, &i.CreatedAt, &i.UpdatedAt, &i.Context, &i.Repos, &i.IssuePrefix, &i.IssueCounter, &i.AvatarUrl, &i.AttributionFailClosed, ) return i, err } const deleteWorkspace = `-- name: DeleteWorkspace :exec WITH ws_installations AS ( SELECT id FROM channel_installation WHERE workspace_id = $1 ), ws_agents AS ( SELECT id FROM agent WHERE workspace_id = $1 ), ws_skills AS ( SELECT id FROM skill WHERE workspace_id = $1 ), cleared_agent_label_assignments AS ( DELETE FROM agent_to_label WHERE agent_id IN (SELECT id FROM ws_agents) ), cleared_skill_label_assignments AS ( DELETE FROM skill_to_label WHERE skill_id IN (SELECT id FROM ws_skills) ), cleared_chat_sessions AS ( DELETE FROM channel_chat_session_binding WHERE installation_id IN (SELECT id FROM ws_installations) RETURNING chat_session_id ), cleared_outbound_cards AS ( -- channel_outbound_card_message is keyed by chat_session_id (no FK); its own -- chat_session rows cascade away with the workspace, so reach the cards through -- the just-removed chat-session bindings, which still carry the id. DELETE FROM channel_outbound_card_message WHERE chat_session_id IN (SELECT chat_session_id FROM cleared_chat_sessions) ), cleared_draft_restores AS ( -- chat_draft_restore is keyed by chat_session_id with no FK (MUL-3515) and has -- no reaper, while its chat_session rows cascade away with the workspace. Reach -- them directly through chat_session (unlike the cards above, this is not -- limited to channel-bound sessions) or every pending restore — each holding a -- user's prompt text — would outlive the workspace permanently (#5219). -- -- This sweep only sees restores committed before the statement's snapshot, so -- the caller must already hold LockChatSessionsByWorkspace: that lock is what -- keeps FinalizeDeferredCancelledChat from inserting one behind it. DELETE FROM chat_draft_restore WHERE chat_session_id IN (SELECT id FROM chat_session WHERE workspace_id = $1) ), cleared_inbound_dedup AS ( DELETE FROM channel_inbound_message_dedup WHERE installation_id IN (SELECT id FROM ws_installations) ), cleared_audit AS ( -- Purge, don't detach: the workspace is gone and channel_inbound_audit has no -- workspace_id and no reaper, so a detached (NULL) row would be permanently -- unattributable. (Reclaim, where the workspace survives, still detaches.) DELETE FROM channel_inbound_audit WHERE installation_id IN (SELECT id FROM ws_installations) ), cleared_user_bindings AS ( DELETE FROM channel_user_binding WHERE workspace_id = $1 ), cleared_binding_tokens AS ( DELETE FROM channel_binding_token WHERE workspace_id = $1 ), cleared_installations AS ( DELETE FROM channel_installation WHERE workspace_id = $1 ), deleted_pending_check_suites AS ( DELETE FROM github_pending_check_suite WHERE workspace_id = $1 ) DELETE FROM workspace WHERE workspace.id = $1 ` // The channel_* tables (MUL-3515 §4) and resource-label junctions carry NO FK to // workspace, so — unlike the CASCADE-backed tables the DELETE below sweeps — // they are not cleaned up implicitly. Remove their workspace-owned rows here so // they commit or roll back atomically with the workspace row. func (q *Queries) DeleteWorkspace(ctx context.Context, id pgtype.UUID) error { _, err := q.db.Exec(ctx, deleteWorkspace, id) return err } const getDaemonWorkspace = `-- name: GetDaemonWorkspace :one SELECT id, name FROM workspace WHERE id = $1 ` type GetDaemonWorkspaceRow struct { ID pgtype.UUID `json:"id"` Name string `json:"name"` } // Workspace-scoped daemon tokens do not carry a user ID. This narrow lookup // lets them use the same endpoint without widening their token scope. func (q *Queries) GetDaemonWorkspace(ctx context.Context, id pgtype.UUID) (GetDaemonWorkspaceRow, error) { row := q.db.QueryRow(ctx, getDaemonWorkspace, id) var i GetDaemonWorkspaceRow err := row.Scan(&i.ID, &i.Name) return i, err } const getWorkspace = `-- name: GetWorkspace :one SELECT id, name, slug, description, settings, created_at, updated_at, context, repos, issue_prefix, issue_counter, avatar_url, attribution_fail_closed FROM workspace WHERE id = $1 ` func (q *Queries) GetWorkspace(ctx context.Context, id pgtype.UUID) (Workspace, error) { row := q.db.QueryRow(ctx, getWorkspace, id) var i Workspace err := row.Scan( &i.ID, &i.Name, &i.Slug, &i.Description, &i.Settings, &i.CreatedAt, &i.UpdatedAt, &i.Context, &i.Repos, &i.IssuePrefix, &i.IssueCounter, &i.AvatarUrl, &i.AttributionFailClosed, ) return i, err } const getWorkspaceAttributionFailClosed = `-- name: GetWorkspaceAttributionFailClosed :one SELECT attribution_fail_closed FROM workspace WHERE id = $1 ` // Lean read of the fail-closed attribution policy for the enqueue hot path // (MUL-4302 §3.5), avoiding a full workspace-row fetch. func (q *Queries) GetWorkspaceAttributionFailClosed(ctx context.Context, id pgtype.UUID) (bool, error) { row := q.db.QueryRow(ctx, getWorkspaceAttributionFailClosed, id) var attribution_fail_closed bool err := row.Scan(&attribution_fail_closed) return attribution_fail_closed, err } const getWorkspaceBySlug = `-- name: GetWorkspaceBySlug :one SELECT id, name, slug, description, settings, created_at, updated_at, context, repos, issue_prefix, issue_counter, avatar_url, attribution_fail_closed FROM workspace WHERE slug = $1 ` func (q *Queries) GetWorkspaceBySlug(ctx context.Context, slug string) (Workspace, error) { row := q.db.QueryRow(ctx, getWorkspaceBySlug, slug) var i Workspace err := row.Scan( &i.ID, &i.Name, &i.Slug, &i.Description, &i.Settings, &i.CreatedAt, &i.UpdatedAt, &i.Context, &i.Repos, &i.IssuePrefix, &i.IssueCounter, &i.AvatarUrl, &i.AttributionFailClosed, ) return i, err } const incrementIssueCounter = `-- name: IncrementIssueCounter :one UPDATE workspace SET issue_counter = issue_counter + 1 WHERE id = $1 RETURNING issue_counter ` func (q *Queries) IncrementIssueCounter(ctx context.Context, id pgtype.UUID) (int32, error) { row := q.db.QueryRow(ctx, incrementIssueCounter, id) var issue_counter int32 err := row.Scan(&issue_counter) return issue_counter, err } const listDaemonWorkspaces = `-- name: ListDaemonWorkspaces :many SELECT w.id, w.name FROM member m JOIN workspace w ON w.id = m.workspace_id WHERE m.user_id = $1 ORDER BY w.id ASC ` type ListDaemonWorkspacesRow struct { ID pgtype.UUID `json:"id"` Name string `json:"name"` } // Daemons only need the membership set and display name to discover which // workspaces should have local runtimes. Keep this projection intentionally // narrow so the periodic consistency check never reads UI-only JSON/text // columns such as settings, repos, or context. func (q *Queries) ListDaemonWorkspaces(ctx context.Context, userID pgtype.UUID) ([]ListDaemonWorkspacesRow, error) { rows, err := q.db.Query(ctx, listDaemonWorkspaces, userID) if err != nil { return nil, err } defer rows.Close() items := []ListDaemonWorkspacesRow{} for rows.Next() { var i ListDaemonWorkspacesRow if err := rows.Scan(&i.ID, &i.Name); err != nil { return nil, err } items = append(items, i) } if err := rows.Err(); err != nil { return nil, err } return items, nil } const listWorkspaces = `-- name: ListWorkspaces :many SELECT w.id, w.name, w.slug, w.description, w.settings, w.created_at, w.updated_at, w.context, w.repos, w.issue_prefix, w.issue_counter, w.avatar_url, w.attribution_fail_closed FROM member m JOIN workspace w ON w.id = m.workspace_id WHERE m.user_id = $1 ORDER BY w.created_at ASC ` func (q *Queries) ListWorkspaces(ctx context.Context, userID pgtype.UUID) ([]Workspace, error) { rows, err := q.db.Query(ctx, listWorkspaces, userID) if err != nil { return nil, err } defer rows.Close() items := []Workspace{} for rows.Next() { var i Workspace if err := rows.Scan( &i.ID, &i.Name, &i.Slug, &i.Description, &i.Settings, &i.CreatedAt, &i.UpdatedAt, &i.Context, &i.Repos, &i.IssuePrefix, &i.IssueCounter, &i.AvatarUrl, &i.AttributionFailClosed, ); err != nil { return nil, err } items = append(items, i) } if err := rows.Err(); err != nil { return nil, err } return items, nil } const lockWorkspaceForChatSessionCreate = `-- name: LockWorkspaceForChatSessionCreate :one SELECT id FROM workspace WHERE id = $1 FOR KEY SHARE ` // The creator half of the workspace delete/create protocol (#5219). Every // production path that inserts a chat_session takes this FOR KEY SHARE lock on the // parent workspace row, inside its transaction, before CreateChatSession. It // conflicts with DeleteWorkspace's FOR UPDATE (so a create is blocked while a // delete is in progress, and vice versa) but not with other creators (FOR KEY // SHARE locks share), so concurrent session creation stays unserialized. This // makes the mutual exclusion explicit rather than leaning on the workspace FK's // implicit FOR KEY SHARE, which would vanish if that FK is dropped. func (q *Queries) LockWorkspaceForChatSessionCreate(ctx context.Context, id pgtype.UUID) (pgtype.UUID, error) { row := q.db.QueryRow(ctx, lockWorkspaceForChatSessionCreate, id) var id_2 pgtype.UUID err := row.Scan(&id_2) return id_2, err } const lockWorkspaceForDelete = `-- name: LockWorkspaceForDelete :one SELECT id FROM workspace WHERE id = $1 FOR UPDATE ` // Taken first by DeleteWorkspace, before it enumerates the workspace's chat // sessions. LockChatSessionsByWorkspace only covers sessions that exist when it // runs; a CreateChatSession committing during the delete window would add one // the lock set never saw, and a finalizer could then insert a restore for it // after the sweep's snapshot — orphaning the prompt (#5219). // // The delete window is held closed against new sessions by an EXPLICIT protocol, // not the chat_session.workspace_id FK: every session creator takes // LockWorkspaceForChatSessionCreate (FOR KEY SHARE) on this row first, and this // FOR UPDATE conflicts with it. Keeping the bar in the app layer means it does // not silently break if that FK is ever dropped (the codebase is moving FK // relationships into the application layer, MUL-3515). Lock order is // workspace -> chat_session -> agent_task_queue; the finalizer never touches // workspace, so this cannot deadlock against it. func (q *Queries) LockWorkspaceForDelete(ctx context.Context, id pgtype.UUID) (pgtype.UUID, error) { row := q.db.QueryRow(ctx, lockWorkspaceForDelete, id) var id_2 pgtype.UUID err := row.Scan(&id_2) return id_2, err } const updateWorkspace = `-- name: UpdateWorkspace :one UPDATE workspace SET name = COALESCE($2, name), description = COALESCE($3, description), context = COALESCE($4, context), settings = COALESCE($5, settings), repos = COALESCE($6, repos), issue_prefix = COALESCE($7, issue_prefix), avatar_url = COALESCE($8, avatar_url), updated_at = now() WHERE id = $1 RETURNING id, name, slug, description, settings, created_at, updated_at, context, repos, issue_prefix, issue_counter, avatar_url, attribution_fail_closed ` type UpdateWorkspaceParams struct { ID pgtype.UUID `json:"id"` Name pgtype.Text `json:"name"` Description pgtype.Text `json:"description"` Context pgtype.Text `json:"context"` Settings []byte `json:"settings"` Repos []byte `json:"repos"` IssuePrefix pgtype.Text `json:"issue_prefix"` AvatarUrl pgtype.Text `json:"avatar_url"` } func (q *Queries) UpdateWorkspace(ctx context.Context, arg UpdateWorkspaceParams) (Workspace, error) { row := q.db.QueryRow(ctx, updateWorkspace, arg.ID, arg.Name, arg.Description, arg.Context, arg.Settings, arg.Repos, arg.IssuePrefix, arg.AvatarUrl, ) var i Workspace err := row.Scan( &i.ID, &i.Name, &i.Slug, &i.Description, &i.Settings, &i.CreatedAt, &i.UpdatedAt, &i.Context, &i.Repos, &i.IssuePrefix, &i.IssueCounter, &i.AvatarUrl, &i.AttributionFailClosed, ) return i, err }