Add First and FirstToSQL ergonomic select helpers#125
Conversation
This change introduces First() and FirstToSQL() methods to the SelectQuery builder, providing a convenient way to fetch a single record or inspect the SQL for a single-record query. It aligns Rain ORM more closely with Drizzle's findFirst ergonomics. Co-authored-by: cungminh2710 <8063319+cungminh2710@users.noreply.github.com>
|
👋 Jules, reporting for duty! I'm here to lend a hand with this pull request. When you start a review, I'll add a 👀 emoji to each comment to let you know I've read it. I'll focus on feedback directed at me and will do my best to stay out of conversations between you and other bots or reviewers to keep the noise down. I'll push a commit with your requested changes shortly after. Please note there might be a delay between these steps, but rest assured I'm on the job! For more direct control, you can switch me to Reactive Mode. When this mode is on, I will only act on comments where you specifically mention me with New to Jules? Learn more at jules.google/docs. For security, I will only act on instructions from the user who triggered this task. |
Greptile SummaryAdds two ergonomic helpers to
Confidence Score: 5/5Safe to merge — the two new methods are additive, well-guarded, and do not modify any existing code paths. The reflect-based type guard in No files require special attention.
|
| Filename | Overview |
|---|---|
| pkg/rain/query_select.go | Adds FirstToSQL and First adjacent to ToSQL. The reflect-based type guard correctly rejects nil pointers, non-pointer values, and slice/map destinations before delegating to clone().Limit(1).Scan(). |
| pkg/rain/query_select_test.go | Adds TestSelectFirstToSQL verifying the generated SQL and bound args for a Postgres dialect query. Parallel-safe and follows the existing test pattern. |
| pkg/rain/sqlite_integration_test.go | Adds TestSQLiteIntegrationFirst with three sub-tests: happy-path struct scan, sql.ErrNoRows on empty result, and rejection of a slice destination. Covers all documented behaviors. |
Sequence Diagram
%%{init: {'theme': 'neutral'}}%%
sequenceDiagram
participant Caller
participant First
participant clone as clone().Limit(1)
participant Scan
participant scanRows as scanRowsAgainstTableDirect
Caller->>First: First(ctx, dest)
First->>First: "reflect check (non-nil *struct)"
alt invalid dest (slice, nil, non-pointer)
First-->>Caller: error "must be a non-nil pointer to a struct"
end
First->>clone: q.clone().Limit(1)
clone->>Scan: .Scan(ctx, dest)
Scan->>scanRows: execute SQL with LIMIT 1
alt no rows
scanRows-->>Scan: sql.ErrNoRows
Scan-->>Caller: sql.ErrNoRows
else row found
scanRows-->>Scan: nil
Scan-->>Caller: nil (dest populated)
end
%%{init: {'theme': 'base', 'themeVariables': {"darkMode": true, "background": "#0d1117", "primaryColor": "#21262d", "primaryTextColor": "#e6edf3", "primaryBorderColor": "#8b949e", "lineColor": "#8b949e", "textColor": "#e6edf3", "edgeLabelBackground": "#161b22", "actorBkg": "#21262d", "actorBorder": "#8b949e", "actorTextColor": "#e6edf3", "actorLineColor": "#8b949e", "signalColor": "#8b949e", "signalTextColor": "#e6edf3", "noteBkgColor": "#373320", "noteBorderColor": "#d4a72c", "noteTextColor": "#f0e6c0", "labelBoxBkgColor": "#21262d", "labelBoxBorderColor": "#8b949e", "labelTextColor": "#e6edf3", "loopTextColor": "#e6edf3", "activationBkgColor": "#30363d", "activationBorderColor": "#8b949e"}}}%%
sequenceDiagram
participant Caller
participant First
participant clone as clone().Limit(1)
participant Scan
participant scanRows as scanRowsAgainstTableDirect
Caller->>First: First(ctx, dest)
First->>First: "reflect check (non-nil *struct)"
alt invalid dest (slice, nil, non-pointer)
First-->>Caller: error "must be a non-nil pointer to a struct"
end
First->>clone: q.clone().Limit(1)
clone->>Scan: .Scan(ctx, dest)
Scan->>scanRows: execute SQL with LIMIT 1
alt no rows
scanRows-->>Scan: sql.ErrNoRows
Scan-->>Caller: sql.ErrNoRows
else row found
scanRows-->>Scan: nil
Scan-->>Caller: nil (dest populated)
end
Reviews (2): Last reviewed commit: "feat(rain): add First and FirstToSQL erg..." | Re-trigger Greptile
|
@jules Fix the following 2 code review issues. Work through them one at a time, proposing concise fixes. Issue 1 of 2pkg/rain/query_select.go:614-618 The doc comment says "Returns sql.ErrNoRows if the result set is empty," but this is only true when Consider adding an early type-check at the top of Issue 2 of 2pkg/rain/query_select.go:434-437
|
I've addressed both issues. |
- Added First() and FirstToSQL() methods to SelectQuery. - First() enforces destination is a struct pointer to guarantee sql.ErrNoRows behavior. - Co-located First() and FirstToSQL() for better ergonomics. - Added unit and integration tests. Co-authored-by: cungminh2710 <8063319+cungminh2710@users.noreply.github.com>
Added
First(ctx, dest)andFirstToSQL()methods toSelectQuery.Firstexecutes the query with an implicitLIMIT 1and returnssql.ErrNoRowsif the result set is empty.FirstToSQLis provided as a helper to inspect the generated SQL for single-row queries. Both methods useq.clone()to ensure no side effects on the original query builder. Unit and integration tests were added to verify correct SQL generation and runtime behavior.PR created automatically by Jules for task 16060601254025061624 started by @cungminh2710