Repository navigation
feat: add select_raw and join_sub to the query builder - #330
Closed
claude-tmgbedu wants to merge 2 commits into
Closed
claude-tmgbedu wants to merge 2 commits into
claude-tmgbedu wants to merge 2 commits into
Conversation
Why: - Downstream listings need a raw select expression with bindings; select() splits on commas and has no way to bind values. - They also need to join a subquery (LEFT JOIN (SELECT ... GROUP BY ...) AS due), which join() cannot express. - Bindings must follow placeholder order across select, join, where and order by; the grammar compiled ORDER BY before GROUP BY and built ON before the joined table. - count() kept selected columns and ORDER BY beside COUNT(*), which Postgres rejects. How: - QueryBuilder.select_raw(expression, bindings) appends a raw SelectExpression carrying its bindings, never split on commas. - QueryBuilder.join_sub(query, alias, column1, equality, column2, bindings, clause) and left_join_sub(...) add a SubqueryJoinClause; query is a builder (its own bindings) or a raw SQL string with bindings. - BaseGrammar compiles select clauses in SQL order and compiles a join's subquery before its ON clause. - aggregate() on an ungrouped query drops the columns and order by with their bindings and no longer mutates the builder; grouped queries are unchanged. - ADR 049 records the decision; grammar tests cover all four dialects, and SQLite and Postgres tests run the listing query, count() and paginate(). Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Codecov Report✅ All modified and coverable lines are covered by tests. 📢 Thoughts on this report? Let us know! |
Why: - Chaining order_by_raw calls joined the fragments with no comma, producing invalid SQL such as "DESClexemes.text". How: - The grammar adds the ", " separator before a raw order fragment when an order is already present, as it already did for column and subquery orders. - Grammar tests cover raw after raw and raw after a column order on all four dialects. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
This was referenced Oct 31, 2026
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Summary
Adds two Laravel query-builder features to the ORM so downstream apps can move raw-SQL listings onto the builder (ADR 049).
select_raw(expression: str, bindings: Sequence[Any] = ()) -> Self: appends a raw select expression with bindings. It is never split on commas. If nothing else is selected, only the raw expression is selected, as in Laravel.join_sub(query, alias, column1, equality, column2, bindings=(), clause="inner") -> Selfandleft_join_sub(query, alias, column1, equality, column2, bindings=()) -> Self: join(subquery) AS "alias" ON ....queryis aQueryBuilder, which uses its own bindings, or a raw SQL string withbindings. Passingbindingswith a builder raisesTypeError.Binding order
_compile_selectnow compiles clauses in SQL order: columns, joins, wheres, group by, having, order by. Before this change, order-by bindings were collected before group-by bindings.process_joinscompiles a join's subquery, and collects its bindings, before theONclause.count() / paginate()
paginate()already copies_joinsinto its count query. The subquery join and its bindings are counted, and theselect_rawcolumns are not.aggregate()(count,sum,min,max,avg,exists) on an ungrouped query now leaves out the selected columns and the order by, along with their bindings, and no longer mutates the builder. Before, Postgres rejectedcount()withcolumn "lexemes.id" must appear in the GROUP BY clause. Queries withgroup_byorhavingbehave as before, which the existing MySQLtest_group_by/test_havingtests assert.Tests
tests/masoniteorm/query/grammars/test_select_raw_join_sub_grammar.py: SQL and bindings for all four dialects, coveringselect_raw,join_subwith a builder,left_join_subwith raw SQL and bindings, and the combined listing query (to_qmark()andto_sql()). Also covers aggregate SQL.tests/masoniteorm/sqlite/builder/test_sqlite_select_raw_join_sub.pyandtests/masoniteorm/postgres/builder/test_postgres_select_raw_join_sub.py: run the listing query (Postgres usesGREATEST(MIN(...), CAST(? AS date))),count(), andpaginate()for page 1 and page 2.uv run pytest tests/masoniteormwith MySQL and Postgres fromdocker-compose.yml: 1122 passed, 6 skipped.ruff checkandruff format --checkare clean.Removed features
None.
🤖 Generated with Claude Code