// Code generated by sqlc. DO NOT EDIT. // versions: // sqlc v1.31.1 // source: github.sql package db import ( "context" "github.com/jackc/pgx/v5/pgtype" ) const createGitHubInstallation = `-- name: CreateGitHubInstallation :one INSERT INTO github_installation ( workspace_id, installation_id, account_login, account_type, account_avatar_url, connected_by_id ) VALUES ( $1, $2, $3, $4, $5, $6 ) ON CONFLICT (workspace_id, installation_id) DO UPDATE SET account_login = EXCLUDED.account_login, account_type = EXCLUDED.account_type, account_avatar_url = EXCLUDED.account_avatar_url, connected_by_id = EXCLUDED.connected_by_id, updated_at = now() RETURNING id, workspace_id, installation_id, account_login, account_type, account_avatar_url, connected_by_id, created_at, updated_at ` type CreateGitHubInstallationParams struct { WorkspaceID pgtype.UUID `json:"workspace_id"` InstallationID int64 `json:"installation_id"` AccountLogin string `json:"account_login"` AccountType string `json:"account_type"` AccountAvatarUrl pgtype.Text `json:"account_avatar_url"` ConnectedByID pgtype.UUID `json:"connected_by_id"` } func (q *Queries) CreateGitHubInstallation(ctx context.Context, arg CreateGitHubInstallationParams) (GithubInstallation, error) { row := q.db.QueryRow(ctx, createGitHubInstallation, arg.WorkspaceID, arg.InstallationID, arg.AccountLogin, arg.AccountType, arg.AccountAvatarUrl, arg.ConnectedByID, ) var i GithubInstallation err := row.Scan( &i.ID, &i.WorkspaceID, &i.InstallationID, &i.AccountLogin, &i.AccountType, &i.AccountAvatarUrl, &i.ConnectedByID, &i.CreatedAt, &i.UpdatedAt, ) return i, err } const deleteGitHubInstallation = `-- name: DeleteGitHubInstallation :exec DELETE FROM github_installation WHERE id = $1 AND workspace_id = $2 ` type DeleteGitHubInstallationParams struct { ID pgtype.UUID `json:"id"` WorkspaceID pgtype.UUID `json:"workspace_id"` } func (q *Queries) DeleteGitHubInstallation(ctx context.Context, arg DeleteGitHubInstallationParams) error { _, err := q.db.Exec(ctx, deleteGitHubInstallation, arg.ID, arg.WorkspaceID) return err } const deleteGitHubInstallationByInstallationID = `-- name: DeleteGitHubInstallationByInstallationID :many DELETE FROM github_installation WHERE installation_id = $1 RETURNING id, workspace_id ` type DeleteGitHubInstallationByInstallationIDRow struct { ID pgtype.UUID `json:"id"` WorkspaceID pgtype.UUID `json:"workspace_id"` } // GitHub-side uninstall/suspend removes trust in the installation entirely, so // drop every workspace binding. Returns one row per deleted binding so the // handler can broadcast to each affected workspace. func (q *Queries) DeleteGitHubInstallationByInstallationID(ctx context.Context, installationID int64) ([]DeleteGitHubInstallationByInstallationIDRow, error) { rows, err := q.db.Query(ctx, deleteGitHubInstallationByInstallationID, installationID) if err != nil { return nil, err } defer rows.Close() items := []DeleteGitHubInstallationByInstallationIDRow{} for rows.Next() { var i DeleteGitHubInstallationByInstallationIDRow if err := rows.Scan(&i.ID, &i.WorkspaceID); err != nil { return nil, err } items = append(items, i) } if err := rows.Err(); err != nil { return nil, err } return items, nil } const deletePendingGitHubInstallation = `-- name: DeletePendingGitHubInstallation :exec DELETE FROM github_pending_installation WHERE installation_id = $1 ` func (q *Queries) DeletePendingGitHubInstallation(ctx context.Context, installationID int64) error { _, err := q.db.Exec(ctx, deletePendingGitHubInstallation, installationID) return err } const getGitHubInstallationByID = `-- name: GetGitHubInstallationByID :one SELECT id, workspace_id, installation_id, account_login, account_type, account_avatar_url, connected_by_id, created_at, updated_at FROM github_installation WHERE id = $1 ` func (q *Queries) GetGitHubInstallationByID(ctx context.Context, id pgtype.UUID) (GithubInstallation, error) { row := q.db.QueryRow(ctx, getGitHubInstallationByID, id) var i GithubInstallation err := row.Scan( &i.ID, &i.WorkspaceID, &i.InstallationID, &i.AccountLogin, &i.AccountType, &i.AccountAvatarUrl, &i.ConnectedByID, &i.CreatedAt, &i.UpdatedAt, ) return i, err } const getGitHubPullRequest = `-- name: GetGitHubPullRequest :one SELECT id, workspace_id, installation_id, repo_owner, repo_name, pr_number, title, state, html_url, branch, author_login, author_avatar_url, merged_at, closed_at, pr_created_at, pr_updated_at, created_at, updated_at, head_sha, mergeable_state, additions, deletions, changed_files, api_mergeable, api_merge_state_status, checks_rollup_state, snapshot_head_sha, snapshot_fetched_at FROM github_pull_request WHERE workspace_id = $1 AND repo_owner = $2 AND repo_name = $3 AND pr_number = $4 ` type GetGitHubPullRequestParams struct { WorkspaceID pgtype.UUID `json:"workspace_id"` RepoOwner string `json:"repo_owner"` RepoName string `json:"repo_name"` PrNumber int32 `json:"pr_number"` } func (q *Queries) GetGitHubPullRequest(ctx context.Context, arg GetGitHubPullRequestParams) (GithubPullRequest, error) { row := q.db.QueryRow(ctx, getGitHubPullRequest, arg.WorkspaceID, arg.RepoOwner, arg.RepoName, arg.PrNumber, ) var i GithubPullRequest err := row.Scan( &i.ID, &i.WorkspaceID, &i.InstallationID, &i.RepoOwner, &i.RepoName, &i.PrNumber, &i.Title, &i.State, &i.HtmlUrl, &i.Branch, &i.AuthorLogin, &i.AuthorAvatarUrl, &i.MergedAt, &i.ClosedAt, &i.PrCreatedAt, &i.PrUpdatedAt, &i.CreatedAt, &i.UpdatedAt, &i.HeadSha, &i.MergeableState, &i.Additions, &i.Deletions, &i.ChangedFiles, &i.ApiMergeable, &i.ApiMergeStateStatus, &i.ChecksRollupState, &i.SnapshotHeadSha, &i.SnapshotFetchedAt, ) return i, err } const getIssuePullRequestCloseAggregate = `-- name: GetIssuePullRequestCloseAggregate :one SELECT COALESCE(SUM(CASE WHEN pr.state IN ('open', 'draft') THEN 1 ELSE 0 END), 0)::bigint AS open_count, COALESCE(SUM(CASE WHEN pr.state = 'merged' AND ipr.close_intent THEN 1 ELSE 0 END), 0)::bigint AS merged_with_close_intent_count FROM github_pull_request pr JOIN issue_pull_request ipr ON ipr.pull_request_id = pr.id WHERE ipr.issue_id = $1 AND NOT ipr.reference_only ` type GetIssuePullRequestCloseAggregateRow struct { OpenCount int64 `json:"open_count"` MergedWithCloseIntentCount int64 `json:"merged_with_close_intent_count"` } // Aggregates the issue's linked PRs into the two counts that gate // auto-advance: how many are still in flight (`open` or `draft`) and how // many merged PRs declared explicit closing intent on the link row. The // webhook auto-advances the issue when open_count = 0 AND // merged_with_close_intent_count > 0. Both the PR state and the link row // (with close_intent) are persisted before this query runs, so the result // is event-agnostic — a link-only sibling closing after a closing-keyword // PR has already merged still resolves the issue. // // reference_only links (a PR that merely mentions the issue identifier in its // body) are excluded: they are hidden from the issue PR list, so they must not // silently gate auto-advance either. An open body-only mention would otherwise // keep open_count > 0 and block the issue from advancing while being invisible // in the UI. (reference_only rows never carry close_intent, so excluding them // does not change merged_with_close_intent_count.) func (q *Queries) GetIssuePullRequestCloseAggregate(ctx context.Context, issueID pgtype.UUID) (GetIssuePullRequestCloseAggregateRow, error) { row := q.db.QueryRow(ctx, getIssuePullRequestCloseAggregate, issueID) var i GetIssuePullRequestCloseAggregateRow err := row.Scan(&i.OpenCount, &i.MergedWithCloseIntentCount) return i, err } const getIssueReviewHeadSha = `-- name: GetIssueReviewHeadSha :one SELECT head_sha FROM ( SELECT pr.head_sha AS head_sha, pr.state AS state, pr.pr_updated_at AS pr_updated_at FROM github_pull_request pr JOIN issue_pull_request ipr ON ipr.pull_request_id = pr.id WHERE ipr.issue_id = $1 AND pr.head_sha <> '' AND NOT ipr.reference_only UNION ALL SELECT pr.head_sha AS head_sha, pr.state AS state, pr.pr_updated_at AS pr_updated_at FROM vcs_pull_request pr JOIN issue_vcs_pull_request ipr ON ipr.pull_request_id = pr.id WHERE ipr.issue_id = $1 AND pr.head_sha <> '' AND NOT ipr.reference_only ) combined ORDER BY (state IN ('open', 'draft')) DESC, pr_updated_at DESC LIMIT 1 ` // Returns the head SHA of the commit currently "under review" for an issue: // the most-recently-updated linked PR that still has an open/draft state and a // non-empty head_sha. Used by the reviewer-loop dedup (TEN-356) so a pending // review task pinned to an old head does not satisfy a request after HEAD // advanced. Prefers in-flight PRs (open/draft) over merged/closed ones so a // stale merged sibling can't shadow the live review target; falls back to the // newest linked PR with a head_sha when none are open. Returns no rows (empty // string) when the issue has no linked PR — callers treat that as "no SHA key" // and dedup on (issue_id, agent_id) alone, preserving pre-TEN-356 behavior. // // Spans both GitHub and self-hosted VCS PRs: a self-hosted PR pushing a new // commit must move the dedup head SHA the same way a GitHub PR does, otherwise // a fresh review round could be merged away against a stale key. // reference_only links are excluded on both arms, matching the PR-list and // close-aggregate queries: a body-only mention is hidden from the list and the // close gate, so it must not win this ORDER BY and become the review dedup head // SHA either, masking the real working PR's SHA. func (q *Queries) GetIssueReviewHeadSha(ctx context.Context, issueID pgtype.UUID) (string, error) { row := q.db.QueryRow(ctx, getIssueReviewHeadSha, issueID) var head_sha string err := row.Scan(&head_sha) return head_sha, err } const getPendingGitHubInstallation = `-- name: GetPendingGitHubInstallation :one SELECT installation_id, account_login, account_type, account_avatar_url, received_at, updated_at FROM github_pending_installation WHERE installation_id = $1 ` func (q *Queries) GetPendingGitHubInstallation(ctx context.Context, installationID int64) (GithubPendingInstallation, error) { row := q.db.QueryRow(ctx, getPendingGitHubInstallation, installationID) var i GithubPendingInstallation err := row.Scan( &i.InstallationID, &i.AccountLogin, &i.AccountType, &i.AccountAvatarUrl, &i.ReceivedAt, &i.UpdatedAt, ) return i, err } const linkIssueToPullRequest = `-- name: LinkIssueToPullRequest :exec INSERT INTO issue_pull_request ( issue_id, pull_request_id, linked_by_type, linked_by_id, close_intent, reference_only ) VALUES ( $1, $2, $4, $5, $3, $6 ) ON CONFLICT (issue_id, pull_request_id) DO UPDATE SET close_intent = CASE WHEN $7 THEN issue_pull_request.close_intent ELSE EXCLUDED.close_intent END, reference_only = CASE WHEN $7 THEN issue_pull_request.reference_only ELSE EXCLUDED.reference_only END ` type LinkIssueToPullRequestParams struct { IssueID pgtype.UUID `json:"issue_id"` PullRequestID pgtype.UUID `json:"pull_request_id"` CloseIntent bool `json:"close_intent"` LinkedByType pgtype.Text `json:"linked_by_type"` LinkedByID pgtype.UUID `json:"linked_by_id"` ReferenceOnly bool `json:"reference_only"` PreserveCloseIntent bool `json:"preserve_close_intent"` } // ===================== // Issue ↔ Pull Request link // ===================== // close_intent reflects the PR's explicit close declaration at the moment // the webhook is allowed to update that intent. Open/edit/merge webhooks use // the current title/body parse result so authors can remove a closing keyword // before merge. Post-terminal edits can opt into preserving the stored value, // keeping the merge-time decision stable. // // reference_only marks a link justified ONLY by a bare body mention (no closing // keyword, no title/branch reference). It follows the same preserve gate as // close_intent so a post-terminal edit can't retroactively hide a PR that did // the work. The issue's PR list filters these out (see ListPullRequestsByIssue). func (q *Queries) LinkIssueToPullRequest(ctx context.Context, arg LinkIssueToPullRequestParams) error { _, err := q.db.Exec(ctx, linkIssueToPullRequest, arg.IssueID, arg.PullRequestID, arg.CloseIntent, arg.LinkedByType, arg.LinkedByID, arg.ReferenceOnly, arg.PreserveCloseIntent, ) return err } const listGitHubInstallationsByInstallationID = `-- name: ListGitHubInstallationsByInstallationID :many SELECT id, workspace_id, installation_id, account_login, account_type, account_avatar_url, connected_by_id, created_at, updated_at FROM github_installation WHERE installation_id = $1 ORDER BY created_at ASC, id ASC ` // One installation_id can be bound to several workspaces; webhook routing lists // every binding and fans the event out to each bound workspace. Ordered oldest // first so processing is deterministic and replay-stable. func (q *Queries) ListGitHubInstallationsByInstallationID(ctx context.Context, installationID int64) ([]GithubInstallation, error) { rows, err := q.db.Query(ctx, listGitHubInstallationsByInstallationID, installationID) if err != nil { return nil, err } defer rows.Close() items := []GithubInstallation{} for rows.Next() { var i GithubInstallation if err := rows.Scan( &i.ID, &i.WorkspaceID, &i.InstallationID, &i.AccountLogin, &i.AccountType, &i.AccountAvatarUrl, &i.ConnectedByID, &i.CreatedAt, &i.UpdatedAt, ); err != nil { return nil, err } items = append(items, i) } if err := rows.Err(); err != nil { return nil, err } return items, nil } const listGitHubInstallationsByWorkspace = `-- name: ListGitHubInstallationsByWorkspace :many SELECT id, workspace_id, installation_id, account_login, account_type, account_avatar_url, connected_by_id, created_at, updated_at FROM github_installation WHERE workspace_id = $1 ORDER BY created_at ASC ` // ===================== // GitHub Installation // ===================== func (q *Queries) ListGitHubInstallationsByWorkspace(ctx context.Context, workspaceID pgtype.UUID) ([]GithubInstallation, error) { rows, err := q.db.Query(ctx, listGitHubInstallationsByWorkspace, workspaceID) if err != nil { return nil, err } defer rows.Close() items := []GithubInstallation{} for rows.Next() { var i GithubInstallation if err := rows.Scan( &i.ID, &i.WorkspaceID, &i.InstallationID, &i.AccountLogin, &i.AccountType, &i.AccountAvatarUrl, &i.ConnectedByID, &i.CreatedAt, &i.UpdatedAt, ); err != nil { return nil, err } items = append(items, i) } if err := rows.Err(); err != nil { return nil, err } return items, nil } const listIssueIDsForPullRequest = `-- name: ListIssueIDsForPullRequest :many SELECT issue_id FROM issue_pull_request WHERE pull_request_id = $1 ` func (q *Queries) ListIssueIDsForPullRequest(ctx context.Context, pullRequestID pgtype.UUID) ([]pgtype.UUID, error) { rows, err := q.db.Query(ctx, listIssueIDsForPullRequest, pullRequestID) if err != nil { return nil, err } defer rows.Close() items := []pgtype.UUID{} for rows.Next() { var issue_id pgtype.UUID if err := rows.Scan(&issue_id); err != nil { return nil, err } items = append(items, issue_id) } if err := rows.Err(); err != nil { return nil, err } return items, nil } const listPullRequestsByIssue = `-- name: ListPullRequestsByIssue :many WITH issue_prs AS ( SELECT pr.id, pr.snapshot_head_sha FROM github_pull_request pr JOIN issue_pull_request ipr ON ipr.pull_request_id = pr.id WHERE ipr.issue_id = $1 AND NOT ipr.reference_only ), checks AS ( SELECT cr.pr_id, COUNT(*)::bigint AS total, SUM(CASE WHEN cr.status = 'completed' AND cr.conclusion IN ('failure','cancelled','timed_out','action_required','startup_failure','stale','error') THEN 1 ELSE 0 END)::bigint AS failed, SUM(CASE WHEN cr.status = 'completed' AND cr.conclusion IN ('success','neutral','skipped') THEN 1 ELSE 0 END)::bigint AS passed, SUM(CASE WHEN cr.status <> 'completed' OR cr.conclusion IS NULL THEN 1 ELSE 0 END)::bigint AS running, COALESCE( array_agg(cr.name) FILTER (WHERE cr.status = 'completed' AND cr.conclusion IN ('failure','cancelled','timed_out','action_required','startup_failure','stale','error')), '{}' )::text[] AS failed_names FROM github_pull_request_check_run cr JOIN issue_prs ip ON ip.id = cr.pr_id WHERE cr.head_sha = ip.snapshot_head_sha AND ip.snapshot_head_sha <> '' GROUP BY cr.pr_id ) SELECT pr.id, pr.workspace_id, pr.installation_id, pr.repo_owner, pr.repo_name, pr.pr_number, pr.title, pr.state, pr.html_url, pr.branch, pr.author_login, pr.author_avatar_url, pr.merged_at, pr.closed_at, pr.pr_created_at, pr.pr_updated_at, pr.head_sha, pr.mergeable_state, pr.additions, pr.deletions, pr.changed_files, pr.api_mergeable, pr.api_merge_state_status, pr.checks_rollup_state, pr.snapshot_head_sha, pr.snapshot_fetched_at, pr.created_at, pr.updated_at, COALESCE(c.total, 0)::bigint AS checks_total, COALESCE(c.passed, 0)::bigint AS checks_passed, COALESCE(c.failed, 0)::bigint AS checks_failed, COALESCE(c.running, 0)::bigint AS checks_running, COALESCE(c.failed_names, '{}')::text[] AS failed_check_names FROM github_pull_request pr JOIN issue_pull_request ipr ON ipr.pull_request_id = pr.id LEFT JOIN checks c ON c.pr_id = pr.id WHERE ipr.issue_id = $1 AND NOT ipr.reference_only ORDER BY pr.pr_created_at DESC ` type ListPullRequestsByIssueRow struct { ID pgtype.UUID `json:"id"` WorkspaceID pgtype.UUID `json:"workspace_id"` InstallationID int64 `json:"installation_id"` RepoOwner string `json:"repo_owner"` RepoName string `json:"repo_name"` PrNumber int32 `json:"pr_number"` Title string `json:"title"` State string `json:"state"` HtmlUrl string `json:"html_url"` Branch pgtype.Text `json:"branch"` AuthorLogin pgtype.Text `json:"author_login"` AuthorAvatarUrl pgtype.Text `json:"author_avatar_url"` MergedAt pgtype.Timestamptz `json:"merged_at"` ClosedAt pgtype.Timestamptz `json:"closed_at"` PrCreatedAt pgtype.Timestamptz `json:"pr_created_at"` PrUpdatedAt pgtype.Timestamptz `json:"pr_updated_at"` HeadSha string `json:"head_sha"` MergeableState pgtype.Text `json:"mergeable_state"` Additions int32 `json:"additions"` Deletions int32 `json:"deletions"` ChangedFiles int32 `json:"changed_files"` ApiMergeable pgtype.Text `json:"api_mergeable"` ApiMergeStateStatus pgtype.Text `json:"api_merge_state_status"` ChecksRollupState pgtype.Text `json:"checks_rollup_state"` SnapshotHeadSha string `json:"snapshot_head_sha"` SnapshotFetchedAt pgtype.Timestamptz `json:"snapshot_fetched_at"` CreatedAt pgtype.Timestamptz `json:"created_at"` UpdatedAt pgtype.Timestamptz `json:"updated_at"` ChecksTotal int64 `json:"checks_total"` ChecksPassed int64 `json:"checks_passed"` ChecksFailed int64 `json:"checks_failed"` ChecksRunning int64 `json:"checks_running"` FailedCheckNames []string `json:"failed_check_names"` } // Returns the issue's linked PRs with the GitHub API snapshot (MUL-5265): the // mergeability verdict, the CI rollup, and per-check counts for the PR's // CURRENT snapshot head SHA. Checks are aggregated from // github_pull_request_check_run — the run-level snapshot written by the API // refresh pipeline — NOT the legacy suite-level webhook aggregation, which is // removed. The `issue_prs` CTE narrows to this issue's PR ids first so the // aggregation only touches check rows for those PRs. Rows for an OLD head are // excluded by the snapshot_head_sha filter. reference_only links (a PR that // merely mentions the issue identifier in its body, with no closing keyword and // no title/branch reference) are filtered out — they are not working PRs. func (q *Queries) ListPullRequestsByIssue(ctx context.Context, issueID pgtype.UUID) ([]ListPullRequestsByIssueRow, error) { rows, err := q.db.Query(ctx, listPullRequestsByIssue, issueID) if err != nil { return nil, err } defer rows.Close() items := []ListPullRequestsByIssueRow{} for rows.Next() { var i ListPullRequestsByIssueRow if err := rows.Scan( &i.ID, &i.WorkspaceID, &i.InstallationID, &i.RepoOwner, &i.RepoName, &i.PrNumber, &i.Title, &i.State, &i.HtmlUrl, &i.Branch, &i.AuthorLogin, &i.AuthorAvatarUrl, &i.MergedAt, &i.ClosedAt, &i.PrCreatedAt, &i.PrUpdatedAt, &i.HeadSha, &i.MergeableState, &i.Additions, &i.Deletions, &i.ChangedFiles, &i.ApiMergeable, &i.ApiMergeStateStatus, &i.ChecksRollupState, &i.SnapshotHeadSha, &i.SnapshotFetchedAt, &i.CreatedAt, &i.UpdatedAt, &i.ChecksTotal, &i.ChecksPassed, &i.ChecksFailed, &i.ChecksRunning, &i.FailedCheckNames, ); err != nil { return nil, err } items = append(items, i) } if err := rows.Err(); err != nil { return nil, err } return items, nil } const unlinkIssueFromPullRequest = `-- name: UnlinkIssueFromPullRequest :exec DELETE FROM issue_pull_request WHERE issue_id = $1 AND pull_request_id = $2 ` type UnlinkIssueFromPullRequestParams struct { IssueID pgtype.UUID `json:"issue_id"` PullRequestID pgtype.UUID `json:"pull_request_id"` } func (q *Queries) UnlinkIssueFromPullRequest(ctx context.Context, arg UnlinkIssueFromPullRequestParams) error { _, err := q.db.Exec(ctx, unlinkIssueFromPullRequest, arg.IssueID, arg.PullRequestID) return err } const updateGitHubInstallationAccountByInstallationID = `-- name: UpdateGitHubInstallationAccountByInstallationID :many UPDATE github_installation SET account_login = $2, account_type = $3, account_avatar_url = $4, updated_at = now() WHERE installation_id = $1 RETURNING id, workspace_id, installation_id, account_login, account_type, account_avatar_url, connected_by_id, created_at, updated_at ` type UpdateGitHubInstallationAccountByInstallationIDParams struct { InstallationID int64 `json:"installation_id"` AccountLogin string `json:"account_login"` AccountType string `json:"account_type"` AccountAvatarUrl pgtype.Text `json:"account_avatar_url"` } // Refresh the GitHub account display metadata across every workspace binding of // an installation (fired by installation.created/new_permissions_accepted/ // unsuspend). Leaves workspace_id and connected_by_id untouched. func (q *Queries) UpdateGitHubInstallationAccountByInstallationID(ctx context.Context, arg UpdateGitHubInstallationAccountByInstallationIDParams) ([]GithubInstallation, error) { rows, err := q.db.Query(ctx, updateGitHubInstallationAccountByInstallationID, arg.InstallationID, arg.AccountLogin, arg.AccountType, arg.AccountAvatarUrl, ) if err != nil { return nil, err } defer rows.Close() items := []GithubInstallation{} for rows.Next() { var i GithubInstallation if err := rows.Scan( &i.ID, &i.WorkspaceID, &i.InstallationID, &i.AccountLogin, &i.AccountType, &i.AccountAvatarUrl, &i.ConnectedByID, &i.CreatedAt, &i.UpdatedAt, ); err != nil { return nil, err } items = append(items, i) } if err := rows.Err(); err != nil { return nil, err } return items, nil } const upsertGitHubPullRequest = `-- name: UpsertGitHubPullRequest :one INSERT INTO github_pull_request ( workspace_id, installation_id, repo_owner, repo_name, pr_number, title, state, html_url, branch, author_login, author_avatar_url, merged_at, closed_at, pr_created_at, pr_updated_at, head_sha, mergeable_state, additions, deletions, changed_files ) VALUES ( $1, $2, $3, $4, $5, $6, $7, $8, $15, $16, $17, $18, $19, $9, $10, $11, $20, $12, $13, $14 ) ON CONFLICT (workspace_id, repo_owner, repo_name, pr_number) DO UPDATE SET installation_id = EXCLUDED.installation_id, title = EXCLUDED.title, state = EXCLUDED.state, html_url = EXCLUDED.html_url, branch = EXCLUDED.branch, author_login = EXCLUDED.author_login, author_avatar_url = EXCLUDED.author_avatar_url, merged_at = EXCLUDED.merged_at, closed_at = EXCLUDED.closed_at, pr_updated_at = EXCLUDED.pr_updated_at, head_sha = EXCLUDED.head_sha, mergeable_state = CASE WHEN COALESCE($21::boolean, FALSE) THEN NULL WHEN EXCLUDED.mergeable_state IS NOT NULL THEN EXCLUDED.mergeable_state ELSE github_pull_request.mergeable_state END, additions = EXCLUDED.additions, deletions = EXCLUDED.deletions, changed_files = EXCLUDED.changed_files, updated_at = now() RETURNING id, workspace_id, installation_id, repo_owner, repo_name, pr_number, title, state, html_url, branch, author_login, author_avatar_url, merged_at, closed_at, pr_created_at, pr_updated_at, created_at, updated_at, head_sha, mergeable_state, additions, deletions, changed_files, api_mergeable, api_merge_state_status, checks_rollup_state, snapshot_head_sha, snapshot_fetched_at ` type UpsertGitHubPullRequestParams struct { WorkspaceID pgtype.UUID `json:"workspace_id"` InstallationID int64 `json:"installation_id"` RepoOwner string `json:"repo_owner"` RepoName string `json:"repo_name"` PrNumber int32 `json:"pr_number"` Title string `json:"title"` State string `json:"state"` HtmlUrl string `json:"html_url"` PrCreatedAt pgtype.Timestamptz `json:"pr_created_at"` PrUpdatedAt pgtype.Timestamptz `json:"pr_updated_at"` HeadSha string `json:"head_sha"` Additions int32 `json:"additions"` Deletions int32 `json:"deletions"` ChangedFiles int32 `json:"changed_files"` Branch pgtype.Text `json:"branch"` AuthorLogin pgtype.Text `json:"author_login"` AuthorAvatarUrl pgtype.Text `json:"author_avatar_url"` MergedAt pgtype.Timestamptz `json:"merged_at"` ClosedAt pgtype.Timestamptz `json:"closed_at"` MergeableState pgtype.Text `json:"mergeable_state"` ClearMergeableState pgtype.Bool `json:"clear_mergeable_state"` } // ===================== // GitHub Pull Request // ===================== // mergeable_state has three-state semantics on UPDATE: // 1. clear_mergeable_state=true → write NULL (state-changing actions like // opened/synchronize/reopened/edited(base) invalidate the prior verdict). // 2. clear_mergeable_state=false, mergeable_state non-null → write the value. // 3. clear_mergeable_state=false, mergeable_state null → preserve existing // column. Metadata events (labeled/assigned/etc.) ship payloads without // mergeability, and silently clobbering a known clean/dirty would lose // information that GitHub only re-computes lazily. // // INSERT path always writes the incoming value (NULL acceptable for a new row). func (q *Queries) UpsertGitHubPullRequest(ctx context.Context, arg UpsertGitHubPullRequestParams) (GithubPullRequest, error) { row := q.db.QueryRow(ctx, upsertGitHubPullRequest, arg.WorkspaceID, arg.InstallationID, arg.RepoOwner, arg.RepoName, arg.PrNumber, arg.Title, arg.State, arg.HtmlUrl, arg.PrCreatedAt, arg.PrUpdatedAt, arg.HeadSha, arg.Additions, arg.Deletions, arg.ChangedFiles, arg.Branch, arg.AuthorLogin, arg.AuthorAvatarUrl, arg.MergedAt, arg.ClosedAt, arg.MergeableState, arg.ClearMergeableState, ) var i GithubPullRequest err := row.Scan( &i.ID, &i.WorkspaceID, &i.InstallationID, &i.RepoOwner, &i.RepoName, &i.PrNumber, &i.Title, &i.State, &i.HtmlUrl, &i.Branch, &i.AuthorLogin, &i.AuthorAvatarUrl, &i.MergedAt, &i.ClosedAt, &i.PrCreatedAt, &i.PrUpdatedAt, &i.CreatedAt, &i.UpdatedAt, &i.HeadSha, &i.MergeableState, &i.Additions, &i.Deletions, &i.ChangedFiles, &i.ApiMergeable, &i.ApiMergeStateStatus, &i.ChecksRollupState, &i.SnapshotHeadSha, &i.SnapshotFetchedAt, ) return i, err } const upsertPendingGitHubInstallation = `-- name: UpsertPendingGitHubInstallation :one INSERT INTO github_pending_installation ( installation_id, account_login, account_type, account_avatar_url ) VALUES ( $1, $2, $3, $4 ) ON CONFLICT (installation_id) DO UPDATE SET account_login = EXCLUDED.account_login, account_type = EXCLUDED.account_type, account_avatar_url = EXCLUDED.account_avatar_url, updated_at = now() RETURNING installation_id, account_login, account_type, account_avatar_url, received_at, updated_at ` type UpsertPendingGitHubInstallationParams struct { InstallationID int64 `json:"installation_id"` AccountLogin string `json:"account_login"` AccountType string `json:"account_type"` AccountAvatarUrl pgtype.Text `json:"account_avatar_url"` } func (q *Queries) UpsertPendingGitHubInstallation(ctx context.Context, arg UpsertPendingGitHubInstallationParams) (GithubPendingInstallation, error) { row := q.db.QueryRow(ctx, upsertPendingGitHubInstallation, arg.InstallationID, arg.AccountLogin, arg.AccountType, arg.AccountAvatarUrl, ) var i GithubPendingInstallation err := row.Scan( &i.InstallationID, &i.AccountLogin, &i.AccountType, &i.AccountAvatarUrl, &i.ReceivedAt, &i.UpdatedAt, ) return i, err }