Skip to content

[Pg] Add support for left and inner lateral join in postgres #1079

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

Conversation

alexvuka1
Copy link
Contributor

No description provided.

@AndriiSherman
Copy link
Member

@alexvuka1 could you please resolve conflicts? Also I'll let @dankochetov to take from here as well. Need his eyes here

@alexvuka1 alexvuka1 force-pushed the feature/GH-420-join-lateral-support branch 2 times, most recently from 46925c6 to a1a8ede Compare August 25, 2023 15:12
@AndriiSherman
Copy link
Member

@alexvuka1 alexvuka1 force-pushed the feature/GH-420-join-lateral-support branch 2 times, most recently from 739c4ff to 014b164 Compare August 26, 2023 01:57
@alexvuka1 alexvuka1 force-pushed the feature/GH-420-join-lateral-support branch from 09a3d73 to d0d3b2b Compare August 29, 2023 18:41
@alexvuka1 alexvuka1 requested a review from dankochetov August 29, 2023 18:41
@nounder
Copy link

nounder commented Oct 1, 2023

@dankochetov just a kind reminder that it's waiting for the review. I'd love to have it move upstream!

Copy link
Contributor

@dankochetov dankochetov left a 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 target to the beta branch (you'll probably have merge conflicts)

@alexvuka1 alexvuka1 force-pushed the feature/GH-420-join-lateral-support branch from 209819b to 47b48f1 Compare October 19, 2023 19:45
@alexvuka1 alexvuka1 changed the base branch from main to beta October 19, 2023 19:45
@alexvuka1
Copy link
Contributor Author

Please change the PR target to the beta branch (you'll probably have merge conflicts)

Rebased and changed target to beta branch.

@pencilcheck
Copy link

In the mean time before this commit is merged, is there a way to use custom join in drizzle now?

@cnrstvns
Copy link

Any update on this? Would love to convert a few lateral subqueries using the raw sql builder to use the query builder.

@afogel
Copy link

afogel commented Jan 30, 2024

@dankochetov Just checking to see what the status on this might be :) would love to start leveraging leftJoinLateral

@xray-robot
Copy link

+1

@AuroPick
Copy link

AuroPick commented Mar 1, 2024

can we merge this?

@johanneskares
Copy link

+1

@LRTNZ
Copy link

LRTNZ commented Mar 13, 2024

@dankochetov Wondering if there are further changes needed to this, as many of us would love to start using this as soon as possible.

@phatphamsensvn
Copy link

I hope to use it soon in the project with drizzle.

@johanneskares
Copy link

@dankochetov would be awesome to merge this.

@MarkoH17
Copy link

Any update on this?

@mfasanya
Copy link

+1

1 similar comment
@tzezar
Copy link

tzezar commented Aug 2, 2024

+1

@afogel
Copy link

afogel commented Aug 3, 2024

@dankochetov @AndriiSherman curious where this might fit into the roadmap?
Thanks,

@daviddutch
Copy link

Yes, this is one step for Drizzle to cover the full SQL spectrum. It's much needed to be able to build related objects.

@Pulstura
Copy link

Hey guys, what's the status with this PR?

@cojo
Copy link

cojo commented Aug 30, 2024

Just adding our 👍 / request for an update on this PR to the list! We would definitely benefit from pulling this in.

@dkmooers
Copy link

dkmooers commented Sep 9, 2024

+1 here - I'd use this immediately also!

@pencilcheck
Copy link

+1 Same

@AlexDaniel
Copy link

Yes, this would be very helpful for optimizing some larger queries that are composed of multiple parts. Not everything can be easily turned into relational queries, so I'm hopeful this will be merged soon!

@aaronfulkerson
Copy link

aaronfulkerson commented Oct 5, 2024

@dankochetov @AndriiSherman curious where this might fit into the roadmap? Thanks,

Would love a status update! Does this PR need work? Is a custom join method possible? I can see how it might be a lot of work to get "custom joins" working with TypeScript if someone were to try using lateral in a custom join for example.

Are we waiting on someone to add lateral to the rest of the supported databases?

@johanneskares
Copy link

+1

@afogel
Copy link

afogel commented Jan 20, 2025

@AndriiSherman @dankochetov circling back to this... this would still be very helpful. Would really appreciate understanding where this falls in the roadmap

@alexvuka1 alexvuka1 requested a review from dankochetov January 29, 2025 08:39
@focux
Copy link

focux commented Feb 11, 2025

I'm not sure why this PR hasn't been merged. These changes are exposing a functionality that is already supported by drizzle and that is used on the query builder.

For anyone who needs lateral query support now, here is a patch based on the changes contained in this PR. All credits to @alexvuka1.

Click here to see the patch
diff --git a/pg-core/query-builders/select.d.ts b/pg-core/query-builders/select.d.ts
index 2a4ebd99c56baf1ed5264844e5d332130f1bfe0b..92f1165027cd23eafb84ac4bcec29f8b9cced478 100644
--- a/pg-core/query-builders/select.d.ts
+++ b/pg-core/query-builders/select.d.ts
@@ -98,7 +98,16 @@ export declare abstract class PgSelectQueryBuilderBase<THKT extends PgSelectHKTB
      *   .leftJoin(pets, eq(users.id, pets.ownerId))
      * ```
      */
-    leftJoin: PgSelectJoinFn<this, TDynamic, "left">;
+    leftJoin: PgSelectJoinFn<this, TDynamic, "left", false>;
+    /**
+	 * For each row of the table, include
+	 * values from a matching row of the joined
+	 * subquery, if there is a matching row. If not,
+	 * all of the columns of the joined subquery
+	 * will be set to null. The lateral keyword allows
+	 * access to columns after the FROM statement.
+	 */
+    leftJoinLateral: PgSelectJoinFn<this, TDynamic, "left", true>;
     /**
      * Executes a `right join` operation by adding another table to the current query.
      *
@@ -126,7 +135,7 @@ export declare abstract class PgSelectQueryBuilderBase<THKT extends PgSelectHKTB
      *   .rightJoin(pets, eq(users.id, pets.ownerId))
      * ```
      */
-    rightJoin: PgSelectJoinFn<this, TDynamic, "right">;
+    rightJoin: PgSelectJoinFn<this, TDynamic, "right", false>;
     /**
      * Executes an `inner join` operation, creating a new table by combining rows from two tables that have matching values.
      *
@@ -154,7 +163,14 @@ export declare abstract class PgSelectQueryBuilderBase<THKT extends PgSelectHKTB
      *   .innerJoin(pets, eq(users.id, pets.ownerId))
      * ```
      */
-    innerJoin: PgSelectJoinFn<this, TDynamic, "inner">;
+    innerJoin: PgSelectJoinFn<this, TDynamic, "inner", false>;
+    /**
+	 * For each row of the table, the joined subquery
+	 * needs to have a matching row, or it will
+	 * be excluded from results. The lateral keyword allows
+	 * access to columns after the FROM statement.
+	 */
+    innerJoinLateral: PgSelectJoinFn<this, TDynamic, "inner", true>;
     /**
      * Executes a `full join` operation by combining rows from two tables into a new table.
      *
@@ -182,7 +198,7 @@ export declare abstract class PgSelectQueryBuilderBase<THKT extends PgSelectHKTB
      *   .fullJoin(pets, eq(users.id, pets.ownerId))
      * ```
      */
-    fullJoin: PgSelectJoinFn<this, TDynamic, "full">;
+    fullJoin: PgSelectJoinFn<this, TDynamic, "full", false>;
     private createSetOperator;
     /**
      * Adds `union` set operator to the query.
diff --git a/pg-core/query-builders/select.js b/pg-core/query-builders/select.js
index ae18634ef1ca087222f9be341eccf7c08594b40c..614b6049de6d8fe72537e2ff9e26f69eb3cb1045 100644
--- a/pg-core/query-builders/select.js
+++ b/pg-core/query-builders/select.js
@@ -97,7 +97,7 @@ class PgSelectQueryBuilderBase extends TypedQueryBuilder {
     this.tableName = getTableLikeName(table);
     this.joinsNotNullableMap = typeof this.tableName === "string" ? { [this.tableName]: true } : {};
   }
-  createJoin(joinType) {
+  createJoin(joinType, lateral = false) {
     return (table, on) => {
       const baseTableName = this.tableName;
       const tableName = getTableLikeName(table);
@@ -126,7 +126,7 @@ class PgSelectQueryBuilderBase extends TypedQueryBuilder {
       if (!this.config.joins) {
         this.config.joins = [];
       }
-      this.config.joins.push({ on, table, joinType, alias: tableName });
+      this.config.joins.push({ on, table, joinType, alias: tableName, lateral });
       if (typeof tableName === "string") {
         switch (joinType) {
           case "left": {
@@ -184,6 +184,17 @@ class PgSelectQueryBuilderBase extends TypedQueryBuilder {
    * ```
    */
   leftJoin = this.createJoin("left");
+
+  /**
+	 * For each row of the table, include
+	 * values from a matching row of the joined
+	 * subquery, if there is a matching row. If not,
+	 * all of the columns of the joined subquery
+	 * will be set to null. The lateral keyword allows
+	 * access to columns after the FROM statement.
+	 */
+	leftJoinLateral = this.createJoin("left", true);
+
   /**
    * Executes a `right join` operation by adding another table to the current query.
    *
@@ -240,6 +251,15 @@ class PgSelectQueryBuilderBase extends TypedQueryBuilder {
    * ```
    */
   innerJoin = this.createJoin("inner");
+
+  /**
+	 * For each row of the table, the joined subquery
+	 * needs to have a matching row, or it will
+	 * be excluded from results. The lateral keyword allows
+	 * access to columns after the FROM statement.
+	 */
+	innerJoinLateral = this.createJoin("inner", true);
+
   /**
    * Executes a `full join` operation by combining rows from two tables into a new table.
    *
diff --git a/pg-core/query-builders/select.types.d.ts b/pg-core/query-builders/select.types.d.ts
index 8c5ef9c39bbbf0f999b3ddc39f8f57a3648aab81..24a8187fcc9a031ad18a324249713d02d073dd49 100644
--- a/pg-core/query-builders/select.types.d.ts
+++ b/pg-core/query-builders/select.types.d.ts
@@ -51,7 +51,7 @@ export interface PgSelectConfig {
     }[];
 }
 export type PgSelectJoin<T extends AnyPgSelectQueryBuilder, TDynamic extends boolean, TJoinType extends JoinType, TJoinedTable extends PgTable | Subquery | PgViewBase | SQL, TJoinedName extends GetSelectTableName<TJoinedTable> = GetSelectTableName<TJoinedTable>> = T extends any ? PgSelectWithout<PgSelectKind<T['_']['hkt'], T['_']['tableName'], AppendToResult<T['_']['tableName'], T['_']['selection'], TJoinedName, TJoinedTable extends Table ? TJoinedTable['_']['columns'] : TJoinedTable extends Subquery ? Assume<TJoinedTable['_']['selectedFields'], SelectedFields> : never, T['_']['selectMode']>, T['_']['selectMode'] extends 'partial' ? T['_']['selectMode'] : 'multiple', AppendToNullabilityMap<T['_']['nullabilityMap'], TJoinedName, TJoinType>, T['_']['dynamic'], T['_']['excludedMethods']>, TDynamic, T['_']['excludedMethods']> : never;
-export type PgSelectJoinFn<T extends AnyPgSelectQueryBuilder, TDynamic extends boolean, TJoinType extends JoinType> = <TJoinedTable extends PgTable | Subquery | PgViewBase | SQL, TJoinedName extends GetSelectTableName<TJoinedTable> = GetSelectTableName<TJoinedTable>>(table: TJoinedTable, on: ((aliases: T['_']['selection']) => SQL | undefined) | SQL | undefined) => PgSelectJoin<T, TDynamic, TJoinType, TJoinedTable, TJoinedName>;
+export type PgSelectJoinFn<T extends AnyPgSelectQueryBuilder, TDynamic extends boolean, TJoinType extends JoinType, TIsLateral extends boolean> = <TJoinedTable extends (TIsLateral extends true ? Subquery | SQL : PgTable | Subquery | PgViewBase | SQL), TJoinedName extends GetSelectTableName<TJoinedTable> = GetSelectTableName<TJoinedTable>>(table: TJoinedTable, on: ((aliases: T['_']['selection']) => SQL | undefined) | SQL | undefined) => PgSelectJoin<T, TDynamic, TJoinType, TJoinedTable, TJoinedName>;
 export type SelectedFieldsFlat = SelectedFieldsFlatBase<PgColumn>;
 export type SelectedFields = SelectedFieldsBase<PgColumn, PgTable>;
 export type SelectedFieldsOrdered = SelectedFieldsOrderedBase<PgColumn>;

Edit:
Forgot to add that I'm using this patch, so I've tested it and it is working as expected.

@AlexDaniel
Copy link

AlexDaniel commented Feb 13, 2025

Another related PR is #1669, which adds support for CROSS JOIN.

Still waiting for both. 🙏🏼🙏🏼🙏🏼

@samishal1998
Copy link

+1

@Sukairo-02 Sukairo-02 changed the base branch from beta to main April 10, 2025 03:12
@Sukairo-02 Sukairo-02 changed the base branch from main to nextver-pr-batch April 10, 2025 03:31
@Sukairo-02 Sukairo-02 merged commit 4e2c326 into drizzle-team:nextver-pr-batch Apr 10, 2025
AndriiSherman added a commit that referenced this pull request Apr 24, 2025
* 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]>
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Projects
None yet
Development

Successfully merging this pull request may close these issues.