@@ -39,6 +39,55 @@ export const apply = async ({
39
39
} ,
40
40
{ } as Record < string , ( typeof types ) [ number ] >
41
41
)
42
+
43
+ const getReturnType = ( fn : PostgresFunction ) : string => {
44
+ // Case 1: `returns table`.
45
+ const tableArgs = fn . args . filter ( ( { mode } ) => mode === 'table' )
46
+ if ( tableArgs . length > 0 ) {
47
+ const argsNameAndType = tableArgs
48
+ . map ( ( { name, type_id } ) => {
49
+ const type = types . find ( ( { id } ) => id === type_id )
50
+ let tsType = 'unknown'
51
+ if ( type ) {
52
+ tsType = pgTypeToTsType ( type . name , { types, schemas, tables, views } )
53
+ }
54
+ return { name, type : tsType }
55
+ } )
56
+ . sort ( ( a , b ) => a . name . localeCompare ( b . name ) )
57
+
58
+ return `{
59
+ ${ argsNameAndType . map ( ( { name, type } ) => `${ JSON . stringify ( name ) } : ${ type } ` ) }
60
+ }`
61
+ }
62
+
63
+ // Case 2: returns a relation's row type.
64
+ const relation = [ ...tables , ...views ] . find ( ( { id } ) => id === fn . return_type_relation_id )
65
+ if ( relation ) {
66
+ return `{
67
+ ${ columnsByTableId [ relation . id ]
68
+ . map (
69
+ ( column ) =>
70
+ `${ JSON . stringify ( column . name ) } : ${ pgTypeToTsType ( column . format , {
71
+ types,
72
+ schemas,
73
+ tables,
74
+ views,
75
+ } ) } ${ column . is_nullable ? '| null' : '' } `
76
+ )
77
+ . sort ( )
78
+ . join ( ',\n' ) }
79
+ }`
80
+ }
81
+
82
+ // Case 3: returns base/array/composite/enum type.
83
+ const type = types . find ( ( { id } ) => id === fn . return_type_id )
84
+ if ( type ) {
85
+ return pgTypeToTsType ( type . name , { types, schemas, tables, views } )
86
+ }
87
+
88
+ return 'unknown'
89
+ }
90
+
42
91
columns
43
92
. filter ( ( c ) => c . table_id in columnsByTableId )
44
93
. sort ( ( { name : a } , { name : b } ) => a . localeCompare ( b ) )
@@ -106,14 +155,7 @@ export type Database = {
106
155
) ,
107
156
...schemaFunctions
108
157
. filter ( ( fn ) => fn . argument_types === table . name )
109
- . map ( ( fn ) => {
110
- const type = typesById [ fn . return_type_id ]
111
- let tsType = 'unknown'
112
- if ( type ) {
113
- tsType = pgTypeToTsType ( type . name , { types, schemas, tables, views } )
114
- }
115
- return `${ JSON . stringify ( fn . name ) } : ${ tsType } | null`
116
- } ) ,
158
+ . map ( ( fn ) => `${ JSON . stringify ( fn . name ) } : ${ getReturnType ( fn ) } | null` ) ,
117
159
] }
118
160
}
119
161
Insert: {
@@ -374,95 +416,79 @@ export type Database = {
374
416
375
417
// Generate all possible function signatures as a union
376
418
const signatures = ( ( ) => {
377
- // Special case: if any function has a single unnamed parameter
378
- const unnamedFns = fns . filter ( ( fn ) => fn . args . some ( ( { name } ) => name === '' ) )
379
- if ( unnamedFns . length > 0 ) {
380
- // Take only the first function with unnamed parameters
381
- const firstUnnamedFn = unnamedFns [ 0 ]
382
- const firstArgType = typesById [ firstUnnamedFn . args [ 0 ] . type_id ]
383
- const tsType = firstArgType
384
- ? pgTypeToTsType ( firstArgType . name , { types, schemas, tables, views } )
385
- : 'unknown'
386
-
387
- const returnType = ( ( ) => {
388
- // Case 1: `returns table`.
389
- const tableArgs = firstUnnamedFn . args . filter ( ( { mode } ) => mode === 'table' )
390
- if ( tableArgs . length > 0 ) {
391
- const argsNameAndType = tableArgs
392
- . map ( ( { name, type_id } ) => {
393
- const type = types . find ( ( { id } ) => id === type_id )
394
- let tsType = 'unknown'
395
- if ( type ) {
396
- tsType = pgTypeToTsType ( type . name , { types, schemas, tables, views } )
397
- }
398
- return { name, type : tsType }
399
- } )
400
- . sort ( ( a , b ) => a . name . localeCompare ( b . name ) )
419
+ const allSignatures : string [ ] = [ ]
420
+
421
+ // First check if we have a no-param function
422
+ const noParamFns = fns . filter ( ( fn ) => fn . args . length === 0 )
423
+ const unnamedFns = fns . filter ( ( fn ) => {
424
+ // Only include unnamed functions that:
425
+ // 1. Have a single unnamed parameter
426
+ // 2. The parameter is of a valid type (json, jsonb, text)
427
+ // 3. All parameters have default values
428
+ const inArgs = fn . args . filter ( ( { mode } ) => VALID_FUNCTION_ARGS_MODE . has ( mode ) )
429
+ return (
430
+ inArgs . length === 1 &&
431
+ inArgs [ 0 ] . name === '' &&
432
+ ( VALID_UNNAMED_FUNCTION_ARG_TYPES . has ( inArgs [ 0 ] . type_id ) ||
433
+ inArgs [ 0 ] . has_default )
434
+ )
435
+ } )
401
436
402
- return `{
403
- ${ argsNameAndType . map (
404
- ( { name , type } ) => ` ${ JSON . stringify ( name ) } : ${ type } `
405
- ) }
406
- }`
407
- }
437
+ // Special case: one no-param function and unnamed param function exist
438
+ if ( noParamFns . length === 1 ) {
439
+ const noParamFn = noParamFns [ 0 ]
440
+ const unnamedWithDefaultsFn = unnamedFns . find ( ( fn ) =>
441
+ fn . args . every ( ( arg ) => arg . has_default )
442
+ )
408
443
409
- // Case 2: returns a relation's row type.
410
- const relation = [ ...tables , ...views ] . find (
411
- ( { id } ) => id === firstUnnamedFn . return_type_relation_id
412
- )
413
- if ( relation ) {
414
- return `{
415
- ${ columnsByTableId [ relation . id ]
416
- . map (
417
- ( column ) =>
418
- `${ JSON . stringify ( column . name ) } : ${ pgTypeToTsType ( column . format , {
419
- types,
420
- schemas,
421
- tables,
422
- views,
423
- } ) } ${ column . is_nullable ? '| null' : '' } `
424
- )
425
- . sort ( )
426
- . join ( ',\n' ) }
427
- }`
428
- }
444
+ // If we have a function with unnamed params that all have defaults, it creates a conflict
445
+ if ( unnamedWithDefaultsFn ) {
446
+ // Only generate the error signature in this case
447
+ const conflictDesc = [
448
+ `${ fnName } ()` ,
449
+ `${ fnName } ( => ${ typesById [ unnamedWithDefaultsFn . args [ 0 ] . type_id ] ?. name || 'unknown' } )` ,
450
+ ]
451
+ . sort ( )
452
+ . join ( ', ' )
429
453
430
- // Case 3: returns base/array/composite/enum type.
431
- const returnType = types . find (
432
- ( { id } ) => id === firstUnnamedFn . return_type_id
433
- )
434
- if ( returnType ) {
435
- return pgTypeToTsType ( returnType . name , { types, schemas, tables, views } )
436
- }
454
+ allSignatures . push ( `{
455
+ Args: Record<PropertyKey, never>
456
+ Returns: { error: true } & "Could not choose the best candidate function between: ${ conflictDesc } . Try renaming the parameters or the function itself in the database so function overloading can be resolved"
457
+ }` )
458
+ } else {
459
+ // No conflict - just add the no params signature
460
+ allSignatures . push ( `{
461
+ Args: Record<PropertyKey, never>
462
+ Returns: ${ getReturnType ( noParamFn ) } ${ noParamFn . is_set_returning_function && noParamFn . returns_multiple_rows ? '[]' : '' }
463
+ }` )
464
+ }
465
+ }
466
+ if ( unnamedFns . length > 0 ) {
467
+ // If we don't have a no-param function, process the unnamed args
468
+ // Take only the first function with unnamed parameters that has a valid type
469
+ const validUnnamedFn = unnamedFns . find (
470
+ ( fn ) =>
471
+ fn . args . length === 1 &&
472
+ fn . args [ 0 ] . name === '' &&
473
+ VALID_UNNAMED_FUNCTION_ARG_TYPES . has ( fn . args [ 0 ] . type_id )
474
+ )
437
475
438
- return 'unknown'
439
- } ) ( )
440
-
441
- return [
442
- `{
443
- Args: { "": ${ tsType } },
444
- Returns: ${ returnType } ${ firstUnnamedFn . is_set_returning_function && firstUnnamedFn . returns_multiple_rows ? '[]' : '' }
445
- ${
446
- firstUnnamedFn . returns_set_of_table
447
- ? `,
448
- SetofOptions: {
449
- from: ${
450
- firstUnnamedFn . args . length > 0 && firstUnnamedFn . args [ 0 ] . table_name
451
- ? JSON . stringify ( typesById [ firstUnnamedFn . args [ 0 ] . type_id ] . format )
452
- : '"*"'
453
- } ,
454
- to: ${ JSON . stringify ( firstUnnamedFn . return_table_name ) } ,
455
- isOneToOne: ${ firstUnnamedFn . returns_multiple_rows ? false : true }
456
- }`
457
- : ''
458
- }
459
- }` ,
460
- ]
476
+ if ( validUnnamedFn ) {
477
+ const firstArgType = typesById [ validUnnamedFn . args [ 0 ] . type_id ]
478
+ const tsType = firstArgType
479
+ ? pgTypeToTsType ( firstArgType . name , { types, schemas, tables, views } )
480
+ : 'unknown'
481
+
482
+ allSignatures . push ( `{
483
+ Args: { "": ${ tsType } }
484
+ Returns: ${ getReturnType ( validUnnamedFn ) } ${ validUnnamedFn . is_set_returning_function && validUnnamedFn . returns_multiple_rows ? '[]' : '' }
485
+ }` )
486
+ }
461
487
}
462
488
463
489
// For functions with named parameters, generate all signatures
464
490
const namedFns = fns . filter ( ( fn ) => ! fn . args . some ( ( { name } ) => name === '' ) )
465
- return namedFns . map ( ( fn ) => {
491
+ namedFns . forEach ( ( fn ) => {
466
492
const inArgs = fn . args . filter ( ( { mode } ) => mode === 'in' )
467
493
const namedInArgs = inArgs
468
494
. filter ( ( arg ) => arg . name !== '' )
@@ -488,112 +514,57 @@ export type Database = {
488
514
. sort ( )
489
515
. join ( ', ' )
490
516
491
- return `{
517
+ allSignatures . push ( `{
492
518
Args: { ${ inArgs
493
519
. map ( ( arg ) => `${ JSON . stringify ( arg . name ) } : unknown` )
494
520
. sort ( )
495
- . join ( ', ' ) } },
521
+ . join ( ', ' ) } }
496
522
Returns: { error: true } & "Could not choose the best candidate function between: ${ conflictDesc } . Try renaming the parameters or the function itself in the database so function overloading can be resolved"
497
- }`
498
- }
499
-
500
- // Generate normal function signature
501
- const returnType = ( ( ) => {
502
- // Case 1: `returns table`.
503
- const tableArgs = fn . args . filter ( ( { mode } ) => mode === 'table' )
504
- if ( tableArgs . length > 0 ) {
505
- const argsNameAndType = tableArgs
506
- . map ( ( { name, type_id } ) => {
507
- const type = types . find ( ( { id } ) => id === type_id )
523
+ }` )
524
+ } else if ( inArgs . length > 0 ) {
525
+ // Generate normal function signature
526
+ const returnType = getReturnType ( fn )
527
+ allSignatures . push ( `{
528
+ Args: ${ `{ ${ inArgs
529
+ . map ( ( { name, type_id, has_default } ) => {
530
+ const type = typesById [ type_id ]
508
531
let tsType = 'unknown'
509
532
if ( type ) {
510
- tsType = pgTypeToTsType ( type . name , { types, schemas, tables, views } )
533
+ tsType = pgTypeToTsType ( type . name , {
534
+ types,
535
+ schemas,
536
+ tables,
537
+ views,
538
+ } )
511
539
}
512
- return { name, type : tsType }
540
+ return ` ${ JSON . stringify ( name ) } ${ has_default ? '?' : '' } : ${ tsType } `
513
541
} )
514
- . sort ( ( a , b ) => a . name . localeCompare ( b . name ) )
515
-
516
- return `{
517
- ${ argsNameAndType . map (
518
- ( { name, type } ) => `${ JSON . stringify ( name ) } : ${ type } `
519
- ) }
520
- }`
521
- }
522
-
523
- // Case 2: returns a relation's row type.
524
- const relation = [ ...tables , ...views ] . find (
525
- ( { id } ) => id === fn . return_type_relation_id
526
- )
527
- if ( relation ) {
528
- return `{
529
- ${ columnsByTableId [ relation . id ]
530
- . map (
531
- ( column ) =>
532
- `${ JSON . stringify ( column . name ) } : ${ pgTypeToTsType ( column . format , {
533
- types,
534
- schemas,
535
- tables,
536
- views,
537
- } ) } ${ column . is_nullable ? '| null' : '' } `
538
- )
539
- . sort ( )
540
- . join ( ',\n' ) }
541
- }`
542
- }
543
-
544
- // Case 3: returns base/array/composite/enum type.
545
- const type = types . find ( ( { id } ) => id === fn . return_type_id )
546
- if ( type ) {
547
- return pgTypeToTsType ( type . name , { types, schemas, tables, views } )
548
- }
549
-
550
- return 'unknown'
551
- } ) ( )
552
-
553
- return `{
554
- Args: ${
555
- inArgs . length === 0
556
- ? 'Record<PropertyKey, never>'
557
- : `{ ${ inArgs
558
- . map ( ( { name, type_id, has_default } ) => {
559
- const type = typesById [ type_id ]
560
- let tsType = 'unknown'
561
- if ( type ) {
562
- tsType = pgTypeToTsType ( type . name , {
563
- types,
564
- schemas,
565
- tables,
566
- views,
567
- } )
568
- }
569
- return `${ JSON . stringify ( name ) } ${ has_default ? '?' : '' } : ${ tsType } `
570
- } )
571
- . sort ( )
572
- . join ( ', ' ) } }`
573
- } ,
574
- Returns: ${ returnType } ${ fn . is_set_returning_function && fn . returns_multiple_rows ? '[]' : '' }
575
- ${
576
- fn . returns_set_of_table
577
- ? `,
578
- SetofOptions: {
579
- from: ${
580
- fn . args . length > 0 && fn . args [ 0 ] . table_name
581
- ? JSON . stringify ( typesById [ fn . args [ 0 ] . type_id ] . format )
582
- : '"*"'
583
- } ,
584
- to: ${ JSON . stringify ( fn . return_table_name ) } ,
585
- isOneToOne: ${ fn . returns_multiple_rows ? false : true }
586
- }`
587
- : ''
588
- }
589
- }`
542
+ . sort ( )
543
+ . join ( ', ' ) } }`}
544
+ Returns: ${ returnType } ${ fn . is_set_returning_function && fn . returns_multiple_rows ? '[]' : '' }
545
+ ${
546
+ fn . returns_set_of_table
547
+ ? `SetofOptions: {
548
+ from: ${
549
+ fn . args . length > 0 && fn . args [ 0 ] . table_name
550
+ ? JSON . stringify ( typesById [ fn . args [ 0 ] . type_id ] . format )
551
+ : '"*"'
552
+ }
553
+ to: ${ JSON . stringify ( fn . return_table_name ) }
554
+ isOneToOne: ${ fn . returns_multiple_rows ? false : true }
555
+ }`
556
+ : ''
557
+ }
558
+ }` )
559
+ }
590
560
} )
561
+
562
+ // Remove duplicates and sort
563
+ return Array . from ( new Set ( allSignatures ) ) . sort ( )
591
564
} ) ( )
592
565
593
566
// Remove duplicates, sort, and join with |
594
- return `${ JSON . stringify ( fnName ) } : ${ Array . from ( new Set ( signatures ) )
595
- . sort ( )
596
- . join ( '\n | ' ) } `
567
+ return `${ JSON . stringify ( fnName ) } : ${ signatures . join ( '\n | ' ) } `
597
568
} )
598
569
} ) ( ) }
599
570
}
0 commit comments