Skip to content

Commit 2263c3c

Browse files
Sukairo-02L-Mario564imhoffdItayBenAmialexvuka1
authored
v0.43.0 (#4397)
* Add support for cross join (#1669) * Add cross join support * Add cross join tests * Updated docs links (docs TBD) * Merged cross join function types into join function types, fixed missing cases for `cross` join type, ordered `crossJoin` test queries * `onIndex` support for `MySQL` `crossJoin`, fixed lack of `onIndex` param description in JSDoc, additional tests, disabled test case failing due to driver's issue --------- Co-authored-by: Sukairo-02 <[email protected]> * Export `PgTextBuilderInitial` type (#1286) * MySQL: Removed .fullJoin() from MySQL Api (#1351) Added new type MySqlJoinType which excludes the option for full joins and is used in the MySQL query builder createJoin function instead of the regular JoinType. Also removed two MySQL select type-tests which included the fullJoin function Co-authored-by: Sukairo-02 <[email protected]> * [Pg] Add support for left and inner lateral join in postgres (#1079) * feat: added support for left and inner lateral join in postgres Signed-off-by: Alex Vukadinov <[email protected]> * chore: updated method descriptions on the new joins Signed-off-by: Alex Vukadinov <[email protected]> * GH-420 allowing sql inside lateral joins and revert package.json * GH-420 reverted package.json empty line at the end * Changed JSDoc [WIP], reversed check --------- Signed-off-by: Alex Vukadinov <[email protected]> Co-authored-by: Sukairo-02 <[email protected]> * Completed `left`, `inner`, `cross` lateral joins in `postgresql`, `mysql`, `gel`, `singlestore`, disabled implicit `schema.table` prefixes in column selections on single table selections for `gel` due to possible errors in subqueries on column name matches in inner and outer queries, added\altered related tests * [SingleStore] Add Connection Attributes and Fix Options (#4417) * add connection attributes to SingleStore driver connection * fix allowing connection strings/options rather than external pool --------- Co-authored-by: Andrii Sherman <[email protected]> * Fix for #2654, related tests (#4353) * Dprint * Bumped version, added changelog, fixed broken test case * fix(3554) Correct spelling of `nowait` flag (#3555) * Fixed `nowait` in `Gel`, `SingleStore`, added change to changelog * Updated changelog * Fixed broken test case * Fixed `nowait` tests in `bun-sql`, `mysql-prefixed` * Add changelog updates --------- Signed-off-by: Alex Vukadinov <[email protected]> Co-authored-by: L-Mario564 <[email protected]> Co-authored-by: Dan Imhoff <[email protected]> Co-authored-by: Itay Ben-Ami <[email protected]> Co-authored-by: Alex Vukadinov <[email protected]> Co-authored-by: Mitchell Adair <[email protected]> Co-authored-by: Andrii Sherman <[email protected]> Co-authored-by: Jacob Elder <[email protected]>
1 parent 9e81def commit 2263c3c

File tree

46 files changed

+3394
-266
lines changed

Some content is hidden

Large Commits have some content hidden by default. Use the searchbox below for content that may be hidden.

46 files changed

+3394
-266
lines changed

changelogs/drizzle-orm/0.43.0.md

+16
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,16 @@
1+
## Features
2+
3+
- Added `cross join` \([#1414](https://github.com/drizzle-team/drizzle-orm/issues/1414)\)
4+
- Added lateral `left`, `inner`, `cross` joins to `PostgreSQL`, `MySQL`, `Gel`, `SingleStore`
5+
- Added drizzle connection attributes to `SingleStore`'s driver instances
6+
7+
## Fixes
8+
9+
- Removed unsupported by dialect `full join` from `MySQL` select api
10+
- Forced `Gel` columns to always have explicit schema & table prefixes due to potential errors caused by lack of such prefix in subquery's selection when there's already a column bearing same name in context
11+
- Added missing export for `PgTextBuilderInitial` type
12+
- Removed outdated `IfNotImported` type check from `SingleStore` driver initializer
13+
- Fixed incorrect type inferrence for insert and update models with non-strict `tsconfig`s \([#2654](https://github.com/drizzle-team/drizzle-orm/issues/2654)\)
14+
- Fixed invalid spelling of `nowait` flag \([#3554](https://github.com/drizzle-team/drizzle-orm/issues/3554)\)
15+
- [Add join lateral support](https://github.com/drizzle-team/drizzle-orm/issues/420)
16+
- [Remove .fullJoin() from MySQL API](https://github.com/drizzle-team/drizzle-orm/issues/1125)

drizzle-orm/package.json

+1-1
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,6 @@
11
{
22
"name": "drizzle-orm",
3-
"version": "0.42.0",
3+
"version": "0.43.0",
44
"description": "Drizzle ORM package for SQL databases",
55
"type": "module",
66
"scripts": {

drizzle-orm/src/gel-core/dialect.ts

+30-23
Original file line numberDiff line numberDiff line change
@@ -195,9 +195,11 @@ export class GelDialect {
195195
* `insert ... returning <selection>`
196196
*
197197
* If `isSingleTable` is true, then columns won't be prefixed with table name
198+
* ^ Temporarily disabled behaviour, see comments within method for a reasoning
198199
*/
199200
private buildSelection(
200201
fields: SelectedFieldsOrdered,
202+
// eslint-disable-next-line @typescript-eslint/no-unused-vars
201203
{ isSingleTable = false }: { isSingleTable?: boolean } = {},
202204
): SQL {
203205
const columnsLen = fields.length;
@@ -211,30 +213,34 @@ export class GelDialect {
211213
} else if (is(field, SQL.Aliased) || is(field, SQL)) {
212214
const query = is(field, SQL.Aliased) ? field.sql : field;
213215

214-
if (isSingleTable) {
215-
chunk.push(
216-
new SQL(
217-
query.queryChunks.map((c) => {
218-
if (is(c, GelColumn)) {
219-
return sql.identifier(this.casing.getColumnCasing(c));
220-
}
221-
return c;
222-
}),
223-
),
224-
);
225-
} else {
226-
chunk.push(query);
227-
}
216+
// Gel throws an error when more than one similarly named columns exist within context instead of preferring the closest one
217+
// thus forcing us to be explicit about column's source
218+
// if (isSingleTable) {
219+
// chunk.push(
220+
// new SQL(
221+
// query.queryChunks.map((c) => {
222+
// if (is(c, GelColumn)) {
223+
// return sql.identifier(this.casing.getColumnCasing(c));
224+
// }
225+
// return c;
226+
// }),
227+
// ),
228+
// );
229+
// } else {
230+
chunk.push(query);
231+
// }
228232

229233
if (is(field, SQL.Aliased)) {
230234
chunk.push(sql` as ${sql.identifier(field.fieldAlias)}`);
231235
}
232236
} else if (is(field, Column)) {
233-
if (isSingleTable) {
234-
chunk.push(sql.identifier(this.casing.getColumnCasing(field)));
235-
} else {
236-
chunk.push(field);
237-
}
237+
// Gel throws an error when more than one similarly named columns exist within context instead of preferring the closest one
238+
// thus forcing us to be explicit about column's source
239+
// if (isSingleTable) {
240+
// chunk.push(sql.identifier(this.casing.getColumnCasing(field)));
241+
// } else {
242+
chunk.push(field);
243+
// }
238244
}
239245

240246
if (i < columnsLen - 1) {
@@ -260,6 +266,7 @@ export class GelDialect {
260266
}
261267
const table = joinMeta.table;
262268
const lateralSql = joinMeta.lateral ? sql` lateral` : undefined;
269+
const onSql = joinMeta.on ? sql` on ${joinMeta.on}` : undefined;
263270

264271
if (is(table, GelTable)) {
265272
const tableName = table[GelTable.Symbol.Name];
@@ -269,7 +276,7 @@ export class GelDialect {
269276
joinsArray.push(
270277
sql`${sql.raw(joinMeta.joinType)} join${lateralSql} ${
271278
tableSchema ? sql`${sql.identifier(tableSchema)}.` : undefined
272-
}${sql.identifier(origTableName)}${alias && sql` ${sql.identifier(alias)}`} on ${joinMeta.on}`,
279+
}${sql.identifier(origTableName)}${alias && sql` ${sql.identifier(alias)}`}${onSql}`,
273280
);
274281
} else if (is(table, View)) {
275282
const viewName = table[ViewBaseConfig].name;
@@ -279,11 +286,11 @@ export class GelDialect {
279286
joinsArray.push(
280287
sql`${sql.raw(joinMeta.joinType)} join${lateralSql} ${
281288
viewSchema ? sql`${sql.identifier(viewSchema)}.` : undefined
282-
}${sql.identifier(origViewName)}${alias && sql` ${sql.identifier(alias)}`} on ${joinMeta.on}`,
289+
}${sql.identifier(origViewName)}${alias && sql` ${sql.identifier(alias)}`}${onSql}`,
283290
);
284291
} else {
285292
joinsArray.push(
286-
sql`${sql.raw(joinMeta.joinType)} join${lateralSql} ${table} on ${joinMeta.on}`,
293+
sql`${sql.raw(joinMeta.joinType)} join${lateralSql} ${table}${onSql}`,
287294
);
288295
}
289296
if (index < joins.length - 1) {
@@ -401,7 +408,7 @@ export class GelDialect {
401408
);
402409
}
403410
if (lockingClause.config.noWait) {
404-
clauseSql.append(sql` no wait`);
411+
clauseSql.append(sql` nowait`);
405412
} else if (lockingClause.config.skipLocked) {
406413
clauseSql.append(sql` skip locked`);
407414
}

drizzle-orm/src/gel-core/query-builders/select.ts

+90-16
Original file line numberDiff line numberDiff line change
@@ -206,12 +206,16 @@ export abstract class GelSelectQueryBuilderBase<
206206
this.joinsNotNullableMap = typeof this.tableName === 'string' ? { [this.tableName]: true } : {};
207207
}
208208

209-
private createJoin<TJoinType extends JoinType>(
209+
private createJoin<
210+
TJoinType extends JoinType,
211+
TIsLateral extends (TJoinType extends 'full' | 'right' ? false : boolean),
212+
>(
210213
joinType: TJoinType,
211-
): GelSelectJoinFn<this, TDynamic, TJoinType> {
214+
lateral: TIsLateral,
215+
): GelSelectJoinFn<this, TDynamic, TJoinType, TIsLateral> {
212216
return (
213217
table: GelTable | Subquery | GelViewBase | SQL,
214-
on: ((aliases: TSelection) => SQL | undefined) | SQL | undefined,
218+
on?: ((aliases: TSelection) => SQL | undefined) | SQL | undefined,
215219
) => {
216220
const baseTableName = this.tableName;
217221
const tableName = getTableLikeName(table);
@@ -250,7 +254,7 @@ export abstract class GelSelectQueryBuilderBase<
250254
this.config.joins = [];
251255
}
252256

253-
this.config.joins.push({ on, table, joinType, alias: tableName });
257+
this.config.joins.push({ on, table, joinType, alias: tableName, lateral });
254258

255259
if (typeof tableName === 'string') {
256260
switch (joinType) {
@@ -265,6 +269,7 @@ export abstract class GelSelectQueryBuilderBase<
265269
this.joinsNotNullableMap[tableName] = true;
266270
break;
267271
}
272+
case 'cross':
268273
case 'inner': {
269274
this.joinsNotNullableMap[tableName] = true;
270275
break;
@@ -297,20 +302,34 @@ export abstract class GelSelectQueryBuilderBase<
297302
*
298303
* ```ts
299304
* // Select all users and their pets
300-
* const usersWithPets: { user: User; pets: Pet | null }[] = await db.select()
305+
* const usersWithPets: { user: User; pets: Pet | null; }[] = await db.select()
301306
* .from(users)
302307
* .leftJoin(pets, eq(users.id, pets.ownerId))
303308
*
304309
* // Select userId and petId
305-
* const usersIdsAndPetIds: { userId: number; petId: number | null }[] = await db.select({
310+
* const usersIdsAndPetIds: { userId: number; petId: number | null; }[] = await db.select({
306311
* userId: users.id,
307312
* petId: pets.id,
308313
* })
309314
* .from(users)
310315
* .leftJoin(pets, eq(users.id, pets.ownerId))
311316
* ```
312317
*/
313-
leftJoin = this.createJoin('left');
318+
leftJoin = this.createJoin('left', false);
319+
320+
/**
321+
* Executes a `left join lateral` operation by adding subquery to the current query.
322+
*
323+
* A `lateral` join allows the right-hand expression to refer to columns from the left-hand side.
324+
*
325+
* Calling this method associates each row of the table with the corresponding row from the joined table, if a match is found. If no matching row exists, it sets all columns of the joined table to null.
326+
*
327+
* See docs: {@link https://orm.drizzle.team/docs/joins#left-join-lateral}
328+
*
329+
* @param table the subquery to join.
330+
* @param on the `on` clause.
331+
*/
332+
leftJoinLateral = this.createJoin('left', true);
314333

315334
/**
316335
* Executes a `right join` operation by adding another table to the current query.
@@ -326,20 +345,20 @@ export abstract class GelSelectQueryBuilderBase<
326345
*
327346
* ```ts
328347
* // Select all users and their pets
329-
* const usersWithPets: { user: User | null; pets: Pet }[] = await db.select()
348+
* const usersWithPets: { user: User | null; pets: Pet; }[] = await db.select()
330349
* .from(users)
331350
* .rightJoin(pets, eq(users.id, pets.ownerId))
332351
*
333352
* // Select userId and petId
334-
* const usersIdsAndPetIds: { userId: number | null; petId: number }[] = await db.select({
353+
* const usersIdsAndPetIds: { userId: number | null; petId: number; }[] = await db.select({
335354
* userId: users.id,
336355
* petId: pets.id,
337356
* })
338357
* .from(users)
339358
* .rightJoin(pets, eq(users.id, pets.ownerId))
340359
* ```
341360
*/
342-
rightJoin = this.createJoin('right');
361+
rightJoin = this.createJoin('right', false);
343362

344363
/**
345364
* Executes an `inner join` operation, creating a new table by combining rows from two tables that have matching values.
@@ -355,20 +374,34 @@ export abstract class GelSelectQueryBuilderBase<
355374
*
356375
* ```ts
357376
* // Select all users and their pets
358-
* const usersWithPets: { user: User; pets: Pet }[] = await db.select()
377+
* const usersWithPets: { user: User; pets: Pet; }[] = await db.select()
359378
* .from(users)
360379
* .innerJoin(pets, eq(users.id, pets.ownerId))
361380
*
362381
* // Select userId and petId
363-
* const usersIdsAndPetIds: { userId: number; petId: number }[] = await db.select({
382+
* const usersIdsAndPetIds: { userId: number; petId: number; }[] = await db.select({
364383
* userId: users.id,
365384
* petId: pets.id,
366385
* })
367386
* .from(users)
368387
* .innerJoin(pets, eq(users.id, pets.ownerId))
369388
* ```
370389
*/
371-
innerJoin = this.createJoin('inner');
390+
innerJoin = this.createJoin('inner', false);
391+
392+
/**
393+
* Executes an `inner join lateral` operation, creating a new table by combining rows from two queries that have matching values.
394+
*
395+
* A `lateral` join allows the right-hand expression to refer to columns from the left-hand side.
396+
*
397+
* Calling this method retrieves rows that have corresponding entries in both joined tables. Rows without matching entries in either table are excluded, resulting in a table that includes only matching pairs.
398+
*
399+
* See docs: {@link https://orm.drizzle.team/docs/joins#inner-join-lateral}
400+
*
401+
* @param table the subquery to join.
402+
* @param on the `on` clause.
403+
*/
404+
innerJoinLateral = this.createJoin('inner', true);
372405

373406
/**
374407
* Executes a `full join` operation by combining rows from two tables into a new table.
@@ -384,20 +417,61 @@ export abstract class GelSelectQueryBuilderBase<
384417
*
385418
* ```ts
386419
* // Select all users and their pets
387-
* const usersWithPets: { user: User | null; pets: Pet | null }[] = await db.select()
420+
* const usersWithPets: { user: User | null; pets: Pet | null; }[] = await db.select()
388421
* .from(users)
389422
* .fullJoin(pets, eq(users.id, pets.ownerId))
390423
*
391424
* // Select userId and petId
392-
* const usersIdsAndPetIds: { userId: number | null; petId: number | null }[] = await db.select({
425+
* const usersIdsAndPetIds: { userId: number | null; petId: number | null; }[] = await db.select({
393426
* userId: users.id,
394427
* petId: pets.id,
395428
* })
396429
* .from(users)
397430
* .fullJoin(pets, eq(users.id, pets.ownerId))
398431
* ```
399432
*/
400-
fullJoin = this.createJoin('full');
433+
fullJoin = this.createJoin('full', false);
434+
435+
/**
436+
* Executes a `cross join` operation by combining rows from two tables into a new table.
437+
*
438+
* Calling this method retrieves all rows from both main and joined tables, merging all rows from each table.
439+
*
440+
* See docs: {@link https://orm.drizzle.team/docs/joins#cross-join}
441+
*
442+
* @param table the table to join.
443+
*
444+
* @example
445+
*
446+
* ```ts
447+
* // Select all users, each user with every pet
448+
* const usersWithPets: { user: User; pets: Pet; }[] = await db.select()
449+
* .from(users)
450+
* .crossJoin(pets)
451+
*
452+
* // Select userId and petId
453+
* const usersIdsAndPetIds: { userId: number; petId: number; }[] = await db.select({
454+
* userId: users.id,
455+
* petId: pets.id,
456+
* })
457+
* .from(users)
458+
* .crossJoin(pets)
459+
* ```
460+
*/
461+
crossJoin = this.createJoin('cross', false);
462+
463+
/**
464+
* Executes a `cross join lateral` operation by combining rows from two queries into a new table.
465+
*
466+
* A `lateral` join allows the right-hand expression to refer to columns from the left-hand side.
467+
*
468+
* Calling this method retrieves all rows from both main and joined queries, merging all rows from each query.
469+
*
470+
* See docs: {@link https://orm.drizzle.team/docs/joins#cross-join-lateral}
471+
*
472+
* @param table the query to join.
473+
*/
474+
crossJoinLateral = this.createJoin('cross', true);
401475

402476
private createSetOperator(
403477
type: SetOperator,

drizzle-orm/src/gel-core/query-builders/select.types.ts

+12-7
Original file line numberDiff line numberDiff line change
@@ -112,13 +112,18 @@ export type GelSelectJoinFn<
112112
T extends AnyGelSelectQueryBuilder,
113113
TDynamic extends boolean,
114114
TJoinType extends JoinType,
115-
> = <
116-
TJoinedTable extends GelTable | Subquery | GelViewBase | SQL,
117-
TJoinedName extends GetSelectTableName<TJoinedTable> = GetSelectTableName<TJoinedTable>,
118-
>(
119-
table: TJoinedTable,
120-
on: ((aliases: T['_']['selection']) => SQL | undefined) | SQL | undefined,
121-
) => GelSelectJoin<T, TDynamic, TJoinType, TJoinedTable, TJoinedName>;
115+
TIsLateral extends boolean,
116+
> = 'cross' extends TJoinType ? <
117+
TJoinedTable extends (TIsLateral extends true ? Subquery | SQL : GelTable | Subquery | GelViewBase | SQL),
118+
TJoinedName extends GetSelectTableName<TJoinedTable> = GetSelectTableName<TJoinedTable>,
119+
>(table: TJoinedTable) => GelSelectJoin<T, TDynamic, TJoinType, TJoinedTable, TJoinedName>
120+
: <
121+
TJoinedTable extends (TIsLateral extends true ? Subquery | SQL : GelTable | Subquery | GelViewBase | SQL),
122+
TJoinedName extends GetSelectTableName<TJoinedTable> = GetSelectTableName<TJoinedTable>,
123+
>(
124+
table: TJoinedTable,
125+
on: ((aliases: T['_']['selection']) => SQL | undefined) | SQL | undefined,
126+
) => GelSelectJoin<T, TDynamic, TJoinType, TJoinedTable, TJoinedName>;
122127

123128
export type SelectedFieldsFlat = SelectedFieldsFlatBase<GelColumn>;
124129

drizzle-orm/src/mysql-core/dialect.ts

+5-4
Original file line numberDiff line numberDiff line change
@@ -330,6 +330,7 @@ export class MySqlDialect {
330330
}
331331
const table = joinMeta.table;
332332
const lateralSql = joinMeta.lateral ? sql` lateral` : undefined;
333+
const onSql = joinMeta.on ? sql` on ${joinMeta.on}` : undefined;
333334

334335
if (is(table, MySqlTable)) {
335336
const tableName = table[MySqlTable.Symbol.Name];
@@ -344,7 +345,7 @@ export class MySqlDialect {
344345
tableSchema ? sql`${sql.identifier(tableSchema)}.` : undefined
345346
}${sql.identifier(origTableName)}${useIndexSql}${forceIndexSql}${ignoreIndexSql}${
346347
alias && sql` ${sql.identifier(alias)}`
347-
} on ${joinMeta.on}`,
348+
}${onSql}`,
348349
);
349350
} else if (is(table, View)) {
350351
const viewName = table[ViewBaseConfig].name;
@@ -354,11 +355,11 @@ export class MySqlDialect {
354355
joinsArray.push(
355356
sql`${sql.raw(joinMeta.joinType)} join${lateralSql} ${
356357
viewSchema ? sql`${sql.identifier(viewSchema)}.` : undefined
357-
}${sql.identifier(origViewName)}${alias && sql` ${sql.identifier(alias)}`} on ${joinMeta.on}`,
358+
}${sql.identifier(origViewName)}${alias && sql` ${sql.identifier(alias)}`}${onSql}`,
358359
);
359360
} else {
360361
joinsArray.push(
361-
sql`${sql.raw(joinMeta.joinType)} join${lateralSql} ${table} on ${joinMeta.on}`,
362+
sql`${sql.raw(joinMeta.joinType)} join${lateralSql} ${table}${onSql}`,
362363
);
363364
}
364365
if (index < joins.length - 1) {
@@ -392,7 +393,7 @@ export class MySqlDialect {
392393
const { config, strength } = lockingClause;
393394
lockingClausesSql = sql` for ${sql.raw(strength)}`;
394395
if (config.noWait) {
395-
lockingClausesSql.append(sql` no wait`);
396+
lockingClausesSql.append(sql` nowait`);
396397
} else if (config.skipLocked) {
397398
lockingClausesSql.append(sql` skip locked`);
398399
}

0 commit comments

Comments
 (0)