@@ -1590,6 +1590,8 @@ export class AbstractQueryGenerator extends AbstractQueryGeneratorTypeScript {
15901590 topLevelInfo ,
15911591 { minifyAliases : options . minifyAliases } ,
15921592 ) ;
1593+ } else if ( include . _isCustomJoin ) {
1594+ joinQuery = this . generateCustomJoin ( include , includeAs , topLevelInfo ) ;
15931595 } else {
15941596 this . _generateSubQueryFilter ( include , includeAs , topLevelInfo ) ;
15951597 joinQuery = this . generateJoin ( include , topLevelInfo , options ) ;
@@ -1710,6 +1712,93 @@ export class AbstractQueryGenerator extends AbstractQueryGeneratorTypeScript {
17101712 ) ;
17111713 }
17121714
1715+ generateCustomJoin ( include , includeAs , topLevelInfo ) {
1716+ const right = include . model ;
1717+ const asRight = includeAs . internalAs ;
1718+ let joinCondition ;
1719+ let joinWhere ;
1720+
1721+ if ( ! include . on ) {
1722+ throw new Error ( 'Custom joins require an "on" condition to be specified' ) ;
1723+ }
1724+
1725+ // Handle the custom join condition
1726+ joinCondition = this . whereItemsQuery ( include . on , {
1727+ mainAlias : asRight ,
1728+ model : include . model ,
1729+ replacements : topLevelInfo . options ?. replacements ,
1730+ } ) ;
1731+
1732+ if ( include . where ) {
1733+ joinWhere = this . whereItemsQuery ( include . where , {
1734+ mainAlias : asRight ,
1735+ model : include . model ,
1736+ replacements : topLevelInfo . options ?. replacements ,
1737+ } ) ;
1738+ if ( joinWhere ) {
1739+ if ( include . or ) {
1740+ joinCondition += ` OR ${ joinWhere } ` ;
1741+ } else {
1742+ joinCondition += ` AND ${ joinWhere } ` ;
1743+ }
1744+ }
1745+ }
1746+
1747+ // Handle alias minification like in generateJoin
1748+ if ( topLevelInfo . options ?. minifyAliases && asRight . length > 63 ) {
1749+ const alias = `%${ topLevelInfo . options . includeAliases . size } ` ;
1750+ topLevelInfo . options . includeAliases . set ( alias , asRight ) ;
1751+ }
1752+
1753+ // Generate attributes for the joined table
1754+ const attributes = [ ] ;
1755+ const rightAttributes = right . modelDefinition . attributes ;
1756+
1757+ // Process each attribute based on include.attributes or all attributes
1758+ const attributesToInclude =
1759+ include . attributes && include . attributes . length > 0
1760+ ? include . attributes
1761+ : Array . from ( rightAttributes . keys ( ) ) ;
1762+
1763+ for ( const attr of attributesToInclude ) {
1764+ if ( typeof attr === 'string' ) {
1765+ // Simple attribute name
1766+ const field = rightAttributes . get ( attr ) ?. columnName || attr ;
1767+ attributes . push (
1768+ `${ this . quoteTable ( asRight ) } .${ this . quoteIdentifier ( field ) } AS ${ this . quoteIdentifier ( `${ asRight } .${ attr } ` ) } ` ,
1769+ ) ;
1770+ } else if ( Array . isArray ( attr ) ) {
1771+ // [field, alias] format
1772+ const [ field , alias ] = attr ;
1773+ if ( typeof field === 'string' ) {
1774+ const columnName = rightAttributes . get ( field ) ?. columnName || field ;
1775+ attributes . push (
1776+ `${ this . quoteTable ( asRight ) } .${ this . quoteIdentifier ( columnName ) } AS ${ this . quoteIdentifier ( `${ asRight } .${ alias } ` ) } ` ,
1777+ ) ;
1778+ } else {
1779+ // Handle complex expressions
1780+ attributes . push (
1781+ `${ this . formatSqlExpression ( field ) } AS ${ this . quoteIdentifier ( `${ asRight } .${ alias } ` ) } ` ,
1782+ ) ;
1783+ }
1784+ }
1785+ }
1786+
1787+ return {
1788+ join : include . required
1789+ ? 'INNER JOIN'
1790+ : include . right && this . _dialect . supports [ 'RIGHT JOIN' ]
1791+ ? 'RIGHT OUTER JOIN'
1792+ : 'LEFT OUTER JOIN' ,
1793+ body : this . quoteTable ( right , { ...topLevelInfo . options , ...include , alias : asRight } ) ,
1794+ condition : joinCondition ,
1795+ attributes : {
1796+ main : attributes ,
1797+ subQuery : [ ] ,
1798+ } ,
1799+ } ;
1800+ }
1801+
17131802 generateJoin ( include , topLevelInfo , options ) {
17141803 const association = include . association ;
17151804 const parent = include . parent ;
0 commit comments