-
-
Notifications
You must be signed in to change notification settings - Fork 740
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Feat: Set operations (union / intersect / except) support #1218
Conversation
Added a new file set-operators.ts with a SetOperatorQueryBuilder class and a SetOperator class The MySqlSelectBuilder now extends the SetOperatorQueryBuilder to inherit the set operator methods Added the export for the set operation functions
This implementation will likely change with the same pattern used in the Mysql implementation
… the user attempts to select different types on both sides of the set operator
…or functions. The types might need some more work
…perator functions - Added a new type 'SetOperatorRestSelect' to properly handle the rest parameter - Added a new generic parameter TRest to properly handle the rest parameter - Added additional type tests for the rest parameters - deleted debigging strings on ValidateShape type
- fixed wring import in pg that prevented test from running - added tests
- Added abstract class PgSetOperatorBuilder that extends the TypedQueryBuilder - Added type helpers for correct type inference including when passing multiple parameters to the function form - PgSelect now extends from PgSetOperatorBuilder instead of TypedQueryBuilder to inherit all its methods
- added SQLiteSetOperatorBuilder and SQLiteSetOperator classes - SQLiteSelectBuilder now extends from SQLiteSetOperatorBuilder to inherit its methods - Exported funciton versions of union, unionAll, intersect and except - Had to add the last generic parameter as any to the in a type for libsql driver
In its most basic form, upon merging this PR, a drizzle user will be able to create queries with union / intersect / except {all} statements with the following syntax: await db.select().from(table)
.union(
db.select().from(table)
); Beyond that, given the following queries: const query1 = db
.select({ id: users.id, name: users.name })
.from(users)
.where(eq(users.id, 1))
.leftJoin(posts, eq(users.id, posts.authorId));
const query2 = db
.select({ id: users.id, name: sql<string>`users.name` })
.from(users)
.where(eq(users.id, 2));
const query3 = db
.select({ id: users.id, name: users.name })
.from(users)
.where(eq(users.id, 3));
const query4 = db
.select({ id: users.id, name: sql<string>`users.name` })
.from(users)
.where(eq(users.id, 4))
.leftJoin(posts, eq(users.id, posts.authorId));
const query5 = db
.select({ id: users.id, name: users.name })
.from(users)
.where(eq(users.id, 5));
const query6 = db
.select({ id: users.id, name: users.name })
.from(users)
.where(eq(users.id, 2)); All of the following are valid drizzle syntax: const result1 = await union(
query1,
query2,
intersect(query3, query4, query5),
query6,
).orderBy(desc(users.id))
const result2 = await union(
union(
union(query1, query2),
intersect(intersect(query3, query4), query5),
),
query6,
)
const result3 = await query1
.union(query2)
.union(query3.intersect(query4).intersect(query5))
.union(query6)
const result4 = await query1
.union(query2)
.union(({ intersect }) => intersect(query3, query4, query5))
.union(query6) And will all result in the same query (MySql and Postgres): "(((select \"users\".\"id\", \"users\".\"name\" from \"users\" left join \"posts\" on \"users\".\"id\" = \"posts\".\"author_id\"
where \"users\".\"id\" = $1) union (select \"id\", users.name from \"users\" where \"users\".\"id\" = $2))
union (((select \"id\", \"name\" from \"users\" where \"users\".\"id\" = $3) intersect (select \"users\".\"id\", users.name
from \"users\" left join \"posts\" on \"users\".\"id\" = \"posts\".\"author_id\" where \"users\".\"id\" = $4))
intersect (select \"id\", \"name\" from \"users\" where \"users\".\"id\" = $5)))
union (select \"id\", \"name\" from \"users\" where \"users\".\"id\" = $6)",
params: [ 1, 2, 3, 4, 5, 2 ] But in SQLite, parenthesis are not allowed, or order by / limit on the left of the set operator clause, so the resulting query will be: "select \"users\".\"id\", \"users\".\"name\" from \"users\" left join \"posts\" on \"users\".\"id\" = \"posts\".\"author_id\"
where \"users\".\"id\" = ? union select \"id\", users.name from \"users\" where \"users\".\"id\" = ?
union select \"id\", \"name\" from \"users\" where \"users\".\"id\" = ? intersect select \"users\".\"id\", users.name
from \"users\" left join \"posts\" on \"users\".\"id\" = \"posts\".\" author_id\" where \"users\".\"id\" = ?
intersect select \"id\", \"name\" from \"users\" where \"users\".\"id\" = ? union select \"id\", \"name\"
from \"users\" where \"users\".\"id\" = ?",
params: [ 1, 2, 3, 4, 5, 2 ] Selecting different columns names, with different types or different number of them will result in a type error that if ignored, will result in a runtime error as it will be invalid SQL syntax. |
- Added coments for type tests - Added runtime check that throws an error if the select have keys in different order - Added tests for the new error throwing behavior
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Please change the PR's destination to beta
instead of main
. There were some query builder type changes, so you will probably have some merge conflicts.
Also, you'd need to update the SetOperator
classes according to the changes in other query builders.
Just want to say that I absolutely love this feature and can't wait to see it released! 🔥 |
The implementation is complete. |
the hype is real, lets gooooooo |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
LET'S GO THE HYPE IS REAL
This PR will close #207