// Code generated by sqlc. DO NOT EDIT. // versions: // sqlc v1.31.1 // source: issue_status.sql package db import ( "context" "github.com/jackc/pgx/v5/pgtype" ) const archiveIssueStatusEntry = `-- name: ArchiveIssueStatusEntry :one UPDATE issue_status SET archived_at = now(), updated_at = now() WHERE id = $1::uuid AND workspace_id = $2::uuid AND is_system = FALSE AND archived_at IS NULL RETURNING id, workspace_id, key, name, description, category, color, is_system, position, archived_at, created_at, updated_at, icon ` type ArchiveIssueStatusEntryParams struct { ID pgtype.UUID `json:"id"` WorkspaceID pgtype.UUID `json:"workspace_id"` } // Built-ins are excluded by is_system: archiving one would delete its // category's behavior definition. // // Archiving retires a status from future use only. Issues already on it keep // it — Effective ignores archived_at, so their behavior is unchanged — while // Resolve rejects it, so nothing new can be assigned to it. func (q *Queries) ArchiveIssueStatusEntry(ctx context.Context, arg ArchiveIssueStatusEntryParams) (IssueStatus, error) { row := q.db.QueryRow(ctx, archiveIssueStatusEntry, arg.ID, arg.WorkspaceID) var i IssueStatus err := row.Scan( &i.ID, &i.WorkspaceID, &i.Key, &i.Name, &i.Description, &i.Category, &i.Color, &i.IsSystem, &i.Position, &i.ArchivedAt, &i.CreatedAt, &i.UpdatedAt, &i.Icon, ) return i, err } const countIssuesUsingStatusKey = `-- name: CountIssuesUsingStatusKey :one SELECT COUNT(*)::bigint FROM issue WHERE workspace_id = $1::uuid AND status = $2::text ` type CountIssuesUsingStatusKeyParams struct { WorkspaceID pgtype.UUID `json:"workspace_id"` Key string `json:"key"` } // Archive precondition, including terminal issues. Call under the catalog lock // so a concurrent status assignment cannot invalidate the empty check. func (q *Queries) CountIssuesUsingStatusKey(ctx context.Context, arg CountIssuesUsingStatusKeyParams) (int64, error) { row := q.db.QueryRow(ctx, countIssuesUsingStatusKey, arg.WorkspaceID, arg.Key) var column_1 int64 err := row.Scan(&column_1) return column_1, err } const createIssueStatusEntry = `-- name: CreateIssueStatusEntry :one INSERT INTO issue_status (workspace_id, key, name, description, category, color, icon, position) VALUES ( $1::uuid, $2::text, $3::text, $4::text, $5::text, $6::text, $7::text, COALESCE( (SELECT MAX(position) + 1 FROM issue_status WHERE workspace_id = $1::uuid AND category = $5::text), 0 ) ) RETURNING id, workspace_id, key, name, description, category, color, is_system, position, archived_at, created_at, updated_at, icon ` type CreateIssueStatusEntryParams struct { WorkspaceID pgtype.UUID `json:"workspace_id"` Key string `json:"key"` Name string `json:"name"` Description string `json:"description"` Category string `json:"category"` Color string `json:"color"` Icon string `json:"icon"` } // Custom statuses only: is_system is never set here, so the canonical-key and // non-archivable CHECK constraints can only ever apply to seeded rows. func (q *Queries) CreateIssueStatusEntry(ctx context.Context, arg CreateIssueStatusEntryParams) (IssueStatus, error) { row := q.db.QueryRow(ctx, createIssueStatusEntry, arg.WorkspaceID, arg.Key, arg.Name, arg.Description, arg.Category, arg.Color, arg.Icon, ) var i IssueStatus err := row.Scan( &i.ID, &i.WorkspaceID, &i.Key, &i.Name, &i.Description, &i.Category, &i.Color, &i.IsSystem, &i.Position, &i.ArchivedAt, &i.CreatedAt, &i.UpdatedAt, &i.Icon, ) return i, err } const deleteIssueStatusEntriesForWorkspace = `-- name: DeleteIssueStatusEntriesForWorkspace :exec DELETE FROM issue_status WHERE workspace_id = $1::uuid ` // No foreign keys by project rule, so workspace teardown cleans up here. func (q *Queries) DeleteIssueStatusEntriesForWorkspace(ctx context.Context, workspaceID pgtype.UUID) error { _, err := q.db.Exec(ctx, deleteIssueStatusEntriesForWorkspace, workspaceID) return err } const getIssueStatusEntryByID = `-- name: GetIssueStatusEntryByID :one SELECT id, workspace_id, key, name, description, category, color, is_system, position, archived_at, created_at, updated_at, icon FROM issue_status WHERE id = $1::uuid AND workspace_id = $2::uuid ` type GetIssueStatusEntryByIDParams struct { ID pgtype.UUID `json:"id"` WorkspaceID pgtype.UUID `json:"workspace_id"` } func (q *Queries) GetIssueStatusEntryByID(ctx context.Context, arg GetIssueStatusEntryByIDParams) (IssueStatus, error) { row := q.db.QueryRow(ctx, getIssueStatusEntryByID, arg.ID, arg.WorkspaceID) var i IssueStatus err := row.Scan( &i.ID, &i.WorkspaceID, &i.Key, &i.Name, &i.Description, &i.Category, &i.Color, &i.IsSystem, &i.Position, &i.ArchivedAt, &i.CreatedAt, &i.UpdatedAt, &i.Icon, ) return i, err } const getIssueStatusEntryByKey = `-- name: GetIssueStatusEntryByKey :one SELECT id, workspace_id, key, name, description, category, color, is_system, position, archived_at, created_at, updated_at, icon FROM issue_status WHERE workspace_id = $1::uuid AND key = $2::text ` type GetIssueStatusEntryByKeyParams struct { WorkspaceID pgtype.UUID `json:"workspace_id"` Key string `json:"key"` } func (q *Queries) GetIssueStatusEntryByKey(ctx context.Context, arg GetIssueStatusEntryByKeyParams) (IssueStatus, error) { row := q.db.QueryRow(ctx, getIssueStatusEntryByKey, arg.WorkspaceID, arg.Key) var i IssueStatus err := row.Scan( &i.ID, &i.WorkspaceID, &i.Key, &i.Name, &i.Description, &i.Category, &i.Color, &i.IsSystem, &i.Position, &i.ArchivedAt, &i.CreatedAt, &i.UpdatedAt, &i.Icon, ) return i, err } const listActiveCustomIssueStatusEntries = `-- name: ListActiveCustomIssueStatusEntries :many SELECT id, workspace_id, key, name, description, category, color, is_system, position, archived_at, created_at, updated_at, icon FROM issue_status WHERE workspace_id = $1::uuid AND category = $2::text AND is_system = FALSE AND archived_at IS NULL ORDER BY position, key ` type ListActiveCustomIssueStatusEntriesParams struct { WorkspaceID pgtype.UUID `json:"workspace_id"` Category string `json:"category"` } // One category's ACTIVE custom statuses — the exact set a reorder must cover. // Read inside the reorder transaction, under the catalog lock, so a status // archived concurrently cannot slip in or out between validation and write. func (q *Queries) ListActiveCustomIssueStatusEntries(ctx context.Context, arg ListActiveCustomIssueStatusEntriesParams) ([]IssueStatus, error) { rows, err := q.db.Query(ctx, listActiveCustomIssueStatusEntries, arg.WorkspaceID, arg.Category) if err != nil { return nil, err } defer rows.Close() items := []IssueStatus{} for rows.Next() { var i IssueStatus if err := rows.Scan( &i.ID, &i.WorkspaceID, &i.Key, &i.Name, &i.Description, &i.Category, &i.Color, &i.IsSystem, &i.Position, &i.ArchivedAt, &i.CreatedAt, &i.UpdatedAt, &i.Icon, ); err != nil { return nil, err } items = append(items, i) } if err := rows.Err(); err != nil { return nil, err } return items, nil } const listIssueStatusEntries = `-- name: ListIssueStatusEntries :many SELECT id, workspace_id, key, name, description, category, color, is_system, position, archived_at, created_at, updated_at, icon FROM issue_status WHERE workspace_id = $1::uuid AND ($2::bool OR archived_at IS NULL) ORDER BY CASE category WHEN 'unstarted' THEN 0 WHEN 'started' THEN 1 WHEN 'done' THEN 2 WHEN 'closed' THEN 3 ELSE 4 END, position, CASE WHEN is_system THEN 0 ELSE 1 END, CASE key WHEN 'backlog' THEN 0 WHEN 'todo' THEN 1 WHEN 'in_progress' THEN 2 WHEN 'in_review' THEN 3 WHEN 'blocked' THEN 4 WHEN 'done' THEN 5 WHEN 'cancelled' THEN 6 ELSE 7 END, key ` type ListIssueStatusEntriesParams struct { WorkspaceID pgtype.UUID `json:"workspace_id"` IncludeArchived bool `json:"include_archived"` } // Position orders both built-in and custom rows inside each lifecycle group. // Built-in order is only a tiebreak for the original seeded positions. func (q *Queries) ListIssueStatusEntries(ctx context.Context, arg ListIssueStatusEntriesParams) ([]IssueStatus, error) { rows, err := q.db.Query(ctx, listIssueStatusEntries, arg.WorkspaceID, arg.IncludeArchived) if err != nil { return nil, err } defer rows.Close() items := []IssueStatus{} for rows.Next() { var i IssueStatus if err := rows.Scan( &i.ID, &i.WorkspaceID, &i.Key, &i.Name, &i.Description, &i.Category, &i.Color, &i.IsSystem, &i.Position, &i.ArchivedAt, &i.CreatedAt, &i.UpdatedAt, &i.Icon, ); err != nil { return nil, err } items = append(items, i) } if err := rows.Err(); err != nil { return nil, err } return items, nil } const listIssueStatusKeysByCategories = `-- name: ListIssueStatusKeysByCategories :many SELECT key FROM issue_status WHERE workspace_id = $1::uuid AND category = ANY($2::text[]) ` type ListIssueStatusKeysByCategoriesParams struct { WorkspaceID pgtype.UUID `json:"workspace_id"` Categories []string `json:"categories"` } // Expands a set of categories to the status keys that belong to them, so a // category filter can be applied as an INDEXED `status = ANY(...)` predicate // instead of wrapping the column in issue_effective_status(), which makes // (workspace_id, status) unusable and forces a full workspace scan. // // ARCHIVED statuses are included on purpose: archiving retires a status from // future assignment but leaves existing issues on it, and those issues must // still appear in their category's column. func (q *Queries) ListIssueStatusKeysByCategories(ctx context.Context, arg ListIssueStatusKeysByCategoriesParams) ([]string, error) { rows, err := q.db.Query(ctx, listIssueStatusKeysByCategories, arg.WorkspaceID, arg.Categories) if err != nil { return nil, err } defer rows.Close() items := []string{} for rows.Next() { var key string if err := rows.Scan(&key); err != nil { return nil, err } items = append(items, key) } if err := rows.Err(); err != nil { return nil, err } return items, nil } const lockIssueStatusCatalog = `-- name: LockIssueStatusCatalog :exec SELECT pg_advisory_xact_lock(hashtextextended($1::uuid::text || ':issue_status', 0)) ` // EXCLUSIVE transaction lock over a workspace's status catalog, held by archive // so no issue can be written onto a status between the in-use census and the // archive itself. pg_advisory_xact_lock (not pg_try_) so a concurrent archive // queues instead of failing. func (q *Queries) LockIssueStatusCatalog(ctx context.Context, workspaceID pgtype.UUID) error { _, err := q.db.Exec(ctx, lockIssueStatusCatalog, workspaceID) return err } const lockIssueStatusCatalogShared = `-- name: LockIssueStatusCatalogShared :exec SELECT pg_advisory_xact_lock_shared(hashtextextended($1::uuid::text || ':issue_status', 0)) ` // SHARED counterpart, taken by a write that puts an issue ON a custom status. // Shared holders do not block each other, so concurrent issue writes keep their // current throughput; they only block against archive's exclusive lock. // // Built-in statuses deliberately do NOT take this lock: a built-in can never be // archived (enforced by issue_status_system_not_archivable), so there is nothing // to race with, and the overwhelmingly common write path stays lock-free. func (q *Queries) LockIssueStatusCatalogShared(ctx context.Context, workspaceID pgtype.UUID) error { _, err := q.db.Exec(ctx, lockIssueStatusCatalogShared, workspaceID) return err } const reorderIssueStatusEntries = `-- name: ReorderIssueStatusEntries :execrows UPDATE issue_status s SET position = ($1::float8[])[v.ordinality], updated_at = now() FROM unnest($4::uuid[]) WITH ORDINALITY AS v(id, ordinality) WHERE s.id = v.id AND s.workspace_id = $2::uuid AND ($3::bool OR s.is_system = FALSE) AND s.archived_at IS NULL ` type ReorderIssueStatusEntriesParams struct { Positions []float64 `json:"positions"` WorkspaceID pgtype.UUID `json:"workspace_id"` IncludeSystem bool `json:"include_system"` Ids []pgtype.UUID `json:"ids"` } // Atomic intra-category reorder. One statement, so a failure leaves the whole // order untouched instead of the partially-applied prefix a per-row PATCH loop // produces. // // Full-catalog callers may reorder built-ins, without changing their semantics. // Legacy custom-only callers preserve the positions occupied by custom rows. // Archived rows remain frozen. func (q *Queries) ReorderIssueStatusEntries(ctx context.Context, arg ReorderIssueStatusEntriesParams) (int64, error) { result, err := q.db.Exec(ctx, reorderIssueStatusEntries, arg.Positions, arg.WorkspaceID, arg.IncludeSystem, arg.Ids, ) if err != nil { return 0, err } return result.RowsAffected(), nil } const seedIssueStatusEntries = `-- name: SeedIssueStatusEntries :exec INSERT INTO issue_status (workspace_id, key, name, description, category, color, is_system, position) VALUES ($1::uuid, 'backlog', 'Backlog', 'Parked. Assigning an issue here never starts an agent run.', 'unstarted', '#6b7280', TRUE, 0), ($1::uuid, 'todo', 'Todo', 'Queued for work. Moving an issue here starts the assigned agent.', 'unstarted', '#6b7280', TRUE, 0), ($1::uuid, 'in_progress', 'In Progress', 'Actively being worked on.', 'started', '#f59e0b', TRUE, 0), ($1::uuid, 'in_review', 'In Review', 'Work delivered, waiting on human review. Finalizes the autopilot run.', 'started', '#22c55e', TRUE, 0), ($1::uuid, 'done', 'Done', 'Completed.', 'done', '#3b82f6', TRUE, 0), ($1::uuid, 'blocked', 'Blocked', 'Stalled on an external dependency.', 'started', '#ef4444', TRUE, 0), ($1::uuid, 'cancelled', 'Cancelled', 'Decided not to do.', 'closed', '#6b7280', TRUE, 0) ON CONFLICT DO NOTHING ` // Issue status catalog (MUL-6243). Each workspace holds the 7 built-in // statuses plus custom ones, all stored in four lifecycle categories. // Idempotent seed of the 7 built-ins. Safe to call concurrently from multiple // pods during a rolling deploy: the unique (workspace_id, key) index makes a // losing racer a no-op rather than an error. Initial positions are 0; the list's // built-in tiebreak preserves the seed order until an admin reorders the group. func (q *Queries) SeedIssueStatusEntries(ctx context.Context, workspaceID pgtype.UUID) error { _, err := q.db.Exec(ctx, seedIssueStatusEntries, workspaceID) return err } const updateIssueStatusEntry = `-- name: UpdateIssueStatusEntry :one UPDATE issue_status SET name = COALESCE($1, name), description = COALESCE($2, description), color = COALESCE($3, color), icon = COALESCE($4, icon), position = COALESCE($5, position), updated_at = now() WHERE id = $6::uuid AND workspace_id = $7::uuid AND is_system = FALSE AND archived_at IS NULL RETURNING id, workspace_id, key, name, description, category, color, is_system, position, archived_at, created_at, updated_at, icon ` type UpdateIssueStatusEntryParams struct { Name pgtype.Text `json:"name"` Description pgtype.Text `json:"description"` Color pgtype.Text `json:"color"` Icon pgtype.Text `json:"icon"` Position pgtype.Float8 `json:"position"` ID pgtype.UUID `json:"id"` WorkspaceID pgtype.UUID `json:"workspace_id"` } // key and category are absent on purpose: both are immutable after create. // The is_system guard is what enforces "built-in name and color are locked" at // the storage layer, so a handler bug cannot rename a built-in. func (q *Queries) UpdateIssueStatusEntry(ctx context.Context, arg UpdateIssueStatusEntryParams) (IssueStatus, error) { row := q.db.QueryRow(ctx, updateIssueStatusEntry, arg.Name, arg.Description, arg.Color, arg.Icon, arg.Position, arg.ID, arg.WorkspaceID, ) var i IssueStatus err := row.Scan( &i.ID, &i.WorkspaceID, &i.Key, &i.Name, &i.Description, &i.Category, &i.Color, &i.IsSystem, &i.Position, &i.ArchivedAt, &i.CreatedAt, &i.UpdatedAt, &i.Icon, ) return i, err }