Skip to content
This repository was archived by the owner on Oct 9, 2025. It is now read-only.

Commit 4dfbb0c

Browse files
committed
feat(types): add isNotNullable optional override to setof function
1 parent c9eee0a commit 4dfbb0c

File tree

8 files changed

+76
-41
lines changed

8 files changed

+76
-41
lines changed

src/select-query-parser/result.ts

Lines changed: 9 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -328,7 +328,10 @@ type ProcessEmbeddedResourceResult<
328328
Schema extends GenericSchema,
329329
Resolved extends {
330330
referencedTable: Pick<GenericTable, 'Row' | 'Relationships'>
331-
relation: GenericRelationship & { match: 'refrel' | 'col' | 'fkname' | 'func' }
331+
relation: GenericRelationship & {
332+
match: 'refrel' | 'col' | 'fkname' | 'func'
333+
isNotNullable?: boolean
334+
}
332335
direction: string
333336
},
334337
Field extends Ast.FieldNode,
@@ -351,7 +354,11 @@ type ProcessEmbeddedResourceResult<
351354
? ProcessedChildren
352355
: ProcessedChildren[]
353356
: Resolved['relation']['isOneToOne'] extends true
354-
? ProcessedChildren | null
357+
? Resolved['relation']['match'] extends 'func'
358+
? Resolved['relation']['isNotNullable'] extends true
359+
? ProcessedChildren
360+
: ProcessedChildren | null
361+
: ProcessedChildren | null
355362
: ProcessedChildren[]
356363
: // If the relation is a self-reference it'll always be considered as reverse relationship
357364
Resolved['relation']['referencedRelation'] extends CurrentTableOrView

src/select-query-parser/utils.ts

Lines changed: 6 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -469,7 +469,12 @@ export type ResolveForwardRelationship<
469469
: false
470470
referencedColumns: []
471471
referencedRelation: FoundEmbededFunctionJoinTableRelation['to']
472-
} & { match: 'func' }
472+
} & {
473+
match: 'func'
474+
isNotNullable: FoundEmbededFunctionJoinTableRelation['isNotNullable'] extends true
475+
? true
476+
: false
477+
}
473478
direction: 'forward'
474479
from: CurrentTableOrView
475480
type: 'found-by-embeded-function'

src/types.ts

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -62,6 +62,7 @@ export type GenericView = GenericUpdatableView | GenericNonUpdatableView
6262

6363
export type GenericSetofOption = {
6464
isOneToOne?: boolean | undefined
65+
isNotNullable?: boolean | undefined
6566
to: string
6667
from: string
6768
}

test/db/00-schema.sql

Lines changed: 10 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -173,6 +173,16 @@ AS $$
173173
SELECT * FROM public.user_profiles WHERE username = user_row.username;
174174
$$;
175175

176+
-- Same definition, but will be used with a type override to pretend this can't ever return null
177+
CREATE OR REPLACE FUNCTION public.get_user_profile_non_nullable(user_row users)
178+
RETURNS SETOF user_profiles
179+
LANGUAGE SQL STABLE
180+
ROWS 1
181+
AS $$
182+
SELECT * FROM public.user_profiles WHERE username = user_row.username;
183+
$$;
184+
185+
176186
CREATE OR REPLACE FUNCTION public.get_messages(channel_row channels)
177187
RETURNS SETOF messages
178188
LANGUAGE SQL STABLE

test/embeded_functions_join.test-d.ts

Lines changed: 10 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -54,6 +54,15 @@ type Schema = Database['public']
5454
}>
5555
expectType<TypeEqual<typeof result, typeof expected>>(true)
5656
}
57+
{
58+
const { data } = await selectQueries.embeded_setof_row_one_function_not_nullable
59+
let result: Exclude<typeof data, null>
60+
let expected: Array<{
61+
username: string
62+
user_called_profile_not_null: Schema['Tables']['user_profiles']['Row']
63+
}>
64+
expectType<TypeEqual<typeof result, typeof expected>>(true)
65+
}
5766

5867
{
5968
const { data } = await selectQueries.embeded_setof_row_one_function_with_fields_selection
@@ -99,6 +108,6 @@ type Schema = Database['public']
99108
{
100109
const { data } = await rpcQueries['function returning a single row embeded table']
101110
let result: Exclude<typeof data, null>
102-
let expected: Array<Schema['Tables']['user_profiles']['Row']>
111+
let expected: Schema['Tables']['user_profiles']['Row']
103112
expectType<TypeEqual<typeof result, typeof expected>>(true)
104113
}

test/embeded_functions_join.ts

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -22,6 +22,10 @@ export const selectParams = {
2222
from: 'users',
2323
select: 'username, user_called_profile:get_user_profile(*)',
2424
},
25+
embeded_setof_row_one_function_not_nullable: {
26+
from: 'users',
27+
select: 'username, user_called_profile_not_null:get_user_profile_non_nullable(*)',
28+
},
2529
embeded_setof_row_one_function_with_fields_selection: {
2630
from: 'users',
2731
select: 'username, user_called_profile:get_user_profile(username)',
@@ -48,6 +52,9 @@ export const selectQueries = {
4852
embeded_setof_row_one_function: postgrest
4953
.from(selectParams.embeded_setof_row_one_function.from)
5054
.select(selectParams.embeded_setof_row_one_function.select),
55+
embeded_setof_row_one_function_not_nullable: postgrest
56+
.from(selectParams.embeded_setof_row_one_function_not_nullable.from)
57+
.select(selectParams.embeded_setof_row_one_function_not_nullable.select),
5158
embeded_setof_row_one_function_with_fields_selection: postgrest
5259
.from(selectParams.embeded_setof_row_one_function_with_fields_selection.from)
5360
.select(selectParams.embeded_setof_row_one_function_with_fields_selection.select),

test/types.generated.ts

Lines changed: 31 additions & 27 deletions
Original file line numberDiff line numberDiff line change
@@ -30,9 +30,7 @@ export type Database = {
3030
}
3131
Functions: {
3232
get_status: {
33-
Args: {
34-
name_param: string
35-
}
33+
Args: { name_param: string }
3634
Returns: Database['public']['Enums']['user_status']
3735
}
3836
}
@@ -480,61 +478,67 @@ export type Database = {
480478
}
481479
Functions: {
482480
function_with_array_param: {
483-
Args: {
484-
param: string[]
485-
}
481+
Args: { param: string[] }
486482
Returns: undefined
487483
}
488484
function_with_optional_param: {
489-
Args: {
490-
param?: string
491-
}
485+
Args: { param?: string }
492486
Returns: string
493487
}
494488
get_messages: {
495489
Args:
496-
| {
497-
channel_row: Database['public']['Tables']['channels']['Row']
498-
}
499-
| {
500-
user_row: Database['public']['Tables']['users']['Row']
501-
}
490+
| { channel_row: Database['public']['Tables']['channels']['Row'] }
491+
| { user_row: Database['public']['Tables']['users']['Row'] }
502492
Returns: {
503493
channel_id: number
504494
data: Json | null
505495
id: number
506496
message: string | null
507497
username: string
508498
}[]
499+
SetofOptions: {
500+
from: 'channels' | 'users'
501+
to: 'messages'
502+
isOneToOne: false
503+
}
509504
}
510505
get_status: {
511-
Args: {
512-
name_param: string
513-
}
506+
Args: { name_param: string }
514507
Returns: Database['public']['Enums']['user_status']
515508
}
516509
get_user_profile: {
517-
Args: {
518-
user_row: Database['public']['Tables']['users']['Row']
510+
Args: { user_row: Database['public']['Tables']['users']['Row'] }
511+
Returns: {
512+
id: number
513+
username: string | null
519514
}
515+
SetofOptions: {
516+
from: 'users'
517+
to: 'user_profiles'
518+
isOneToOne: true
519+
}
520+
}
521+
get_user_profile_non_nullable: {
522+
Args: { user_row: Database['public']['Tables']['users']['Row'] }
520523
Returns: {
521524
id: number
522525
username: string | null
523-
}[]
526+
}
527+
SetofOptions: {
528+
from: 'users'
529+
to: 'user_profiles'
530+
isOneToOne: true
531+
}
524532
}
525533
get_username_and_status: {
526-
Args: {
527-
name_param: string
528-
}
534+
Args: { name_param: string }
529535
Returns: {
530536
username: string
531537
status: Database['public']['Enums']['user_status']
532538
}[]
533539
}
534540
offline_user: {
535-
Args: {
536-
name_param: string
537-
}
541+
Args: { name_param: string }
538542
Returns: Database['public']['Enums']['user_status']
539543
}
540544
void_func: {

test/types.override.ts

Lines changed: 2 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -44,17 +44,9 @@ export type Database = MergeDeep<
4444
}
4545
}
4646
Functions: {
47-
get_messages: {
47+
get_user_profile_non_nullable: {
4848
SetofOptions: {
49-
to: 'messages'
50-
from: 'channels' | 'users'
51-
}
52-
}
53-
get_user_profile: {
54-
SetofOptions: {
55-
to: 'user_profiles'
56-
from: 'users'
57-
isOneToOne: true
49+
isNotNullable: true
5850
}
5951
}
6052
}

0 commit comments

Comments
 (0)