feat(orm): select_sub subquery-as-column + Laravel-style add_select + table()#170
feat(orm): select_sub subquery-as-column + Laravel-style add_select + table()#170tmgbedu wants to merge 2 commits into
Conversation
Codecov Report❌ Patch coverage is 📢 Thoughts on this report? Let us know! |
✅ Code Review Verdict: APPROVE (posted as comment — GH blocks self-approval)Reviewed for correctness, binding order, alias emission, and regression. All gates reproduced locally on the PR branch. Verified
Gates reproduced (PR worktree)
Non-blocking notes
No correctness bugs found. Recommend merge (per split order #168 → #169 → this). |
🔄 Revising verdict → Request changes on
|
… table()
- select_sub(subquery, alias): the subquery-as-column primitive. Renders
(subquery) AS alias with bindings in SELECT position; accepts a builder
or a callable that builds one. Matches Laravel selectSub(query, as).
- add_select(*columns): Laravel's variadic column-adder. Appends plain
string columns with a dedup guard; an associative {alias: subquery} entry
(string key + queryable value) delegates to select_sub, seeding
select({table}.*) first when nothing is selected yet. A string value
under a string key stays a plain column.
- table(name): sets the target table on a bare builder; backs the callable
form of select_sub.
- Migrate the *Through/BelongsToMany with-count callers from the old
add_select(alias, callable) shape to select_sub(callable, alias).
- process_columns: route the subquery alias through subquery_alias_string()
instead of a hardcoded AS (single source of alias emission; SQL unchanged).
Existing string-based select() is unchanged. Covered by grammar-level
to_sql()/to_qmark() with binding-order assertions across all four dialects,
plus a real sqlite suite incl. the Laravel addSelect({alias: subquery})
acceptance case. End-to-end relationship with-count remains gated on the
async count() fix (#886).
dd369f5 to
1107a49
Compare
✅ Re-review (post force-push
|
Replace raw conn.execute CREATE TABLE / INSERT with the framework's Schema builder and Model.insert() for the categories/posts and accounts/logins fixtures. Same data and same assertions (incl. the Laravel addSelect last_login_at acceptance case).
✅ Light re-review (fixture change
|
Summary
Follow-up B from splitting #168, updated per review (#909) to match Laravel's
addSelectsemantics.select_sub(subquery, alias)— the subquery-as-column primitive. Renders(subquery) AS aliaswith bindings in SELECT position (beforeWHERE). Accepts aQueryBuilderor a callable that builds one. Mirrors LaravelselectSub($query, $as).add_select(*columns)— Laravel's variadic column-adder (was previously a swapped-arg alias ofselect_sub; that shape is gone). Appends plain string columns (variadic or a single list) with a dedup guard. An associative{alias: subquery}entry (string key + queryable value) delegates toselect_sub, seedingselect(f"{table}.*")first when nothing is selected yet. A string value under a string key stays a plain column (Laravel parity).table(name)— sets the target table on a bare builder; backs the callable form ofselect_sub.*Through/BelongsToManywith-count callers moved from the oldadd_select(alias, callable)shape toselect_sub(callable, alias).process_columnsnow routes the subquery alias throughsubquery_alias_string()(single source of alias emission; SQL byte-identical — the helper returnsAS {alias}in every dialect today).Acceptance case (from review) works:
Tests
tests/masoniteorm/query/grammars/test_select_sub_grammar.py—select_sub(builder + callable, non-builderTypeError, qmark binding order) andadd_select(variadic, single list, dedup guard, assoc→select_subwith{table}.*seeding, assoc callable, string-value-under-string-key stays a column, mixed list, bare-subquery-without-aliasTypeError, assoc qmark binding order) — all across 4 dialects.tests/masoniteorm/sqlite/builder/test_sqlite_select_sub.py— real sqlite:select_subbuilder/callable,add_selectvariadic, the Laravel{alias: subquery}acceptance case (real data),{table}.*seeding,table(), and a join-shapedselect_sub(the migrated with-count call shape) compiling + executing.Gates
uv run pytest --ignore=tests/masoniteorm/postgres --cov→ 1820 passed / 7 skipped, coverage 78.96% (≥ fail_under 68).ruff check+ruff format --checkclean.select(). No remaining callers of the oldadd_select(alias, subquery)shape (verified by grep).Not in scope
End-to-end relationship with-count is gated on the async
count()coroutine issue — tracked as #886 (fix in progress on a branch stacked atop this one). This PR asserts the with-count construction path viaselect_sub.