@@ -273,6 +273,12 @@ export type RelationsRecord = Record<string, AnyRelation>;
273273export type EmptyRelations = { } ;
274274export type AnyRelations = TablesRelationalConfig ;
275275
276+ /** Function that builds column SQL given the aliased table */
277+ export type RelationColumnBuilder = ( table : SQL ) => SQL ;
278+
279+ /** A relation column can be either a Column or a builder function */
280+ export type RelationColumnValue = Column < any > | RelationColumnBuilder ;
281+
276282export abstract class Relation <
277283 TTargetTableName extends string = string ,
278284> {
@@ -281,8 +287,8 @@ export abstract class Relation<
281287 declare public readonly relationType : 'many' | 'one' ;
282288
283289 fieldName ! : string ;
284- sourceColumns ! : Column < any > [ ] ;
285- targetColumns ! : Column < any > [ ] ;
290+ sourceColumns ! : RelationColumnValue [ ] ;
291+ targetColumns ! : RelationColumnValue [ ] ;
286292 alias : string | undefined ;
287293 where : AnyTableFilter | undefined ;
288294 sourceTable ! : SchemaEntry ;
@@ -330,30 +336,38 @@ export class One<
330336 this . alias = config ?. alias ;
331337 this . where = config ?. where ;
332338 if ( config ?. from ) {
333- this . sourceColumns = ( ( Array . isArray ( config . from )
339+ this . sourceColumns = ( Array . isArray ( config . from )
334340 ? config . from
335- : [ config . from ] ) as RelationsBuilderColumnBase [ ] ) . map ( ( it : RelationsBuilderColumnBase ) => {
336- this . throughTable ??= it . _ . through ? tables [ it . _ . through . _ . tableName ] ! as SchemaEntry : undefined ;
337- this . sourceColumnTableNames . push ( it . _ . tableName ) ;
338- return it . _ . column as Column ;
341+ : [ config . from ] ) . map ( ( it : RelationConfigValue ) => {
342+ if ( isRelationsBuilderColumnBase ( it ) ) {
343+ this . throughTable ??= it . _ . through ? tables [ it . _ . through . _ . tableName ] ! as SchemaEntry : undefined ;
344+ this . sourceColumnTableNames . push ( it . _ . tableName ) ;
345+ return it . _ . column as Column ;
346+ }
347+ return it ; // It's a function, pass through
339348 } ) ;
340349 }
341350 if ( config ?. to ) {
342351 this . targetColumns = ( Array . isArray ( config . to )
343352 ? config . to
344- : [ config . to ] ) . map ( ( it : RelationsBuilderColumnBase ) => {
345- this . throughTable ??= it . _ . through ? tables [ it . _ . through . _ . tableName ] ! as SchemaEntry : undefined ;
346- this . targetColumnTableNames . push ( it . _ . tableName ) ;
347- return it . _ . column as Column ;
353+ : [ config . to ] ) . map ( ( it : RelationConfigValue ) => {
354+ if ( isRelationsBuilderColumnBase ( it ) ) {
355+ this . throughTable ??= it . _ . through ? tables [ it . _ . through . _ . tableName ] ! as SchemaEntry : undefined ;
356+ this . targetColumnTableNames . push ( it . _ . tableName ) ;
357+ return it . _ . column as Column ;
358+ }
359+ return it ; // It's a function, pass through
348360 } ) ;
349361 }
350362
351363 if ( this . throughTable ) {
352364 this . through = {
353- source : ( Array . isArray ( config ?. from ) ? config . from : config ?. from ? [ config . from ] : [ ] ) . map ( (
354- c ,
355- ) => c . _ . through ! ) ,
356- target : ( Array . isArray ( config ?. to ) ? config . to : config ?. to ? [ config . to ] : [ ] ) . map ( ( c ) => c . _ . through ! ) ,
365+ source : ( Array . isArray ( config ?. from ) ? config . from : config ?. from ? [ config . from ] : [ ] )
366+ . filter ( isRelationsBuilderColumnBase )
367+ . map ( ( c ) => c . _ . through ! ) ,
368+ target : ( Array . isArray ( config ?. to ) ? config . to : config ?. to ? [ config . to ] : [ ] )
369+ . filter ( isRelationsBuilderColumnBase )
370+ . map ( ( c ) => c . _ . through ! ) ,
357371 } ;
358372 }
359373 this . optional = ( config ?. optional ?? true ) as TOptional ;
@@ -378,29 +392,37 @@ export class Many<TTargetTableName extends string> extends Relation<TTargetTable
378392 this . alias = config ?. alias ;
379393 this . where = config ?. where ;
380394 if ( config ?. from ) {
381- this . sourceColumns = ( ( Array . isArray ( config . from )
395+ this . sourceColumns = ( Array . isArray ( config . from )
382396 ? config . from
383- : [ config . from ] ) as RelationsBuilderColumnBase [ ] ) . map ( ( it : RelationsBuilderColumnBase ) => {
384- this . throughTable ??= it . _ . through ? tables [ it . _ . through . _ . tableName ] ! as SchemaEntry : undefined ;
385- this . sourceColumnTableNames . push ( it . _ . tableName ) ;
386- return it . _ . column as Column ;
397+ : [ config . from ] ) . map ( ( it : RelationConfigValue ) => {
398+ if ( isRelationsBuilderColumnBase ( it ) ) {
399+ this . throughTable ??= it . _ . through ? tables [ it . _ . through . _ . tableName ] ! as SchemaEntry : undefined ;
400+ this . sourceColumnTableNames . push ( it . _ . tableName ) ;
401+ return it . _ . column as Column ;
402+ }
403+ return it ; // It's a function, pass through
387404 } ) ;
388405 }
389406 if ( config ?. to ) {
390407 this . targetColumns = ( Array . isArray ( config . to )
391408 ? config . to
392- : [ config . to ] ) . map ( ( it : RelationsBuilderColumnBase ) => {
393- this . throughTable ??= it . _ . through ? tables [ it . _ . through . _ . tableName ] ! as SchemaEntry : undefined ;
394- this . targetColumnTableNames . push ( it . _ . tableName ) ;
395- return it . _ . column as Column ;
409+ : [ config . to ] ) . map ( ( it : RelationConfigValue ) => {
410+ if ( isRelationsBuilderColumnBase ( it ) ) {
411+ this . throughTable ??= it . _ . through ? tables [ it . _ . through . _ . tableName ] ! as SchemaEntry : undefined ;
412+ this . targetColumnTableNames . push ( it . _ . tableName ) ;
413+ return it . _ . column as Column ;
414+ }
415+ return it ; // It's a function, pass through
396416 } ) ;
397417 }
398418 if ( this . throughTable ) {
399419 this . through = {
400- source : ( Array . isArray ( config ?. from ) ? config . from : config ?. from ? [ config . from ] : [ ] ) . map ( (
401- c ,
402- ) => c . _ . through ! ) ,
403- target : ( Array . isArray ( config ?. to ) ? config . to : config ?. to ? [ config . to ] : [ ] ) . map ( ( c ) => c . _ . through ! ) ,
420+ source : ( Array . isArray ( config ?. from ) ? config . from : config ?. from ? [ config . from ] : [ ] )
421+ . filter ( isRelationsBuilderColumnBase )
422+ . map ( ( c ) => c . _ . through ! ) ,
423+ target : ( Array . isArray ( config ?. to ) ? config . to : config ?. to ? [ config . to ] : [ ] )
424+ . filter ( isRelationsBuilderColumnBase )
425+ . map ( ( c ) => c . _ . through ! ) ,
404426 } ;
405427 }
406428 }
@@ -1017,9 +1039,15 @@ export type AnyTableFilter = TableFilter<
10171039 FieldSelection
10181040> ;
10191041
1042+ export type RelationConfigValue = RelationsBuilderColumnBase | RelationColumnBuilder ;
1043+
1044+ function isRelationsBuilderColumnBase ( value : RelationConfigValue ) : value is RelationsBuilderColumnBase {
1045+ return typeof value !== 'function' && '_' in value ;
1046+ }
1047+
10201048export interface OneConfig < TTargetTable extends SchemaEntry , TOptional extends boolean > {
1021- from ?: RelationsBuilderColumnBase | [ RelationsBuilderColumnBase , ...RelationsBuilderColumnBase [ ] ] ;
1022- to ?: RelationsBuilderColumnBase | [ RelationsBuilderColumnBase , ...RelationsBuilderColumnBase [ ] ] ;
1049+ from ?: RelationConfigValue | [ RelationConfigValue , ...RelationConfigValue [ ] ] ;
1050+ to ?: RelationConfigValue | [ RelationConfigValue , ...RelationConfigValue [ ] ] ;
10231051 where ?: TableFilter < TTargetTable > ;
10241052 optional ?: TOptional ;
10251053 alias ?: string ;
@@ -1031,8 +1059,8 @@ export type AnyOneConfig = OneConfig<
10311059> ;
10321060
10331061export interface ManyConfig < TTargetTable extends SchemaEntry > {
1034- from ?: RelationsBuilderColumnBase | [ RelationsBuilderColumnBase , ...RelationsBuilderColumnBase [ ] ] ;
1035- to ?: RelationsBuilderColumnBase | [ RelationsBuilderColumnBase , ...RelationsBuilderColumnBase [ ] ] ;
1062+ from ?: RelationConfigValue | [ RelationConfigValue , ...RelationConfigValue [ ] ] ;
1063+ to ?: RelationConfigValue | [ RelationConfigValue , ...RelationConfigValue [ ] ] ;
10361064 where ?: TableFilter < TTargetTable > ;
10371065 alias ?: string ;
10381066}
@@ -1537,6 +1565,13 @@ export interface BuiltRelationFilters {
15371565 joinCondition ?: SQL ;
15381566}
15391567
1568+ function buildColumnSQL ( col : RelationColumnValue , table : SQL , casing : CasingCache ) : SQL {
1569+ if ( typeof col === 'function' ) {
1570+ return col ( table ) ;
1571+ }
1572+ return sql `${ table } .${ sql . identifier ( casing . getColumnCasing ( col ) ) } ` ;
1573+ }
1574+
15401575export function relationToSQL (
15411576 casing : CasingCache ,
15421577 relation : Relation ,
@@ -1549,7 +1584,7 @@ export function relationToSQL(
15491584 const t = relation . through ! . source [ i ] ! ;
15501585
15511586 return eq (
1552- sql `${ sourceTable } . ${ sql . identifier ( casing . getColumnCasing ( s ) ) } ` ,
1587+ buildColumnSQL ( s , sql `${ sourceTable } ` , casing ) ,
15531588 sql `${ throughTable ! } .${ sql . identifier ( is ( t . _ . column , Column ) ? casing . getColumnCasing ( t . _ . column ) : t . _ . key ) } ` ,
15541589 ) ;
15551590 } ) ;
@@ -1559,7 +1594,7 @@ export function relationToSQL(
15591594
15601595 return eq (
15611596 sql `${ throughTable ! } .${ sql . identifier ( is ( t . _ . column , Column ) ? casing . getColumnCasing ( t . _ . column ) : t . _ . key ) } ` ,
1562- sql `${ targetTable } . ${ sql . identifier ( casing . getColumnCasing ( s ) ) } ` ,
1597+ buildColumnSQL ( s , sql `${ targetTable } ` , casing ) ,
15631598 ) ;
15641599 } ) ;
15651600
@@ -1578,8 +1613,8 @@ export function relationToSQL(
15781613 const t = relation . targetColumns [ i ] ! ;
15791614
15801615 return eq (
1581- sql `${ sourceTable } . ${ sql . identifier ( casing . getColumnCasing ( s ) ) } ` ,
1582- sql `${ targetTable } . ${ sql . identifier ( casing . getColumnCasing ( t ) ) } ` ,
1616+ buildColumnSQL ( s , sql `${ sourceTable } ` , casing ) ,
1617+ buildColumnSQL ( t , sql `${ targetTable } ` , casing ) ,
15831618 ) ;
15841619 } ) ;
15851620
0 commit comments