Skip to content

Commit fee2a82

Browse files
authored
Merge pull request #575 from supabase/fix/handle-relationship-duplicates
fix(types): add deduplication helpers for retro-compatibility
2 parents cc9344a + fe72d45 commit fee2a82

File tree

3 files changed

+162
-7
lines changed

3 files changed

+162
-7
lines changed

src/select-query-parser/utils.ts

+37-7
Original file line numberDiff line numberDiff line change
@@ -14,6 +14,23 @@ export type IsAny<T> = 0 extends 1 & T ? true : false
1414

1515
export type SelectQueryError<Message extends string> = { error: true } & Message
1616

17+
/*
18+
** Because of pg-meta types generation there is some cases where a same relationship can be duplicated
19+
** if the relation is across schemas and views this ensure that we dedup those relations and treat them
20+
** as postgrest would.
21+
** This is no longer the case and has been patched here: https://github.com/supabase/postgres-meta/pull/809
22+
** But we still need this for retro-compatibilty with older generated types
23+
** TODO: Remove this in next major version
24+
*/
25+
export type DeduplicateRelationships<T extends readonly unknown[]> = T extends readonly [
26+
infer First,
27+
...infer Rest
28+
]
29+
? First extends Rest[number]
30+
? DeduplicateRelationships<Rest extends readonly unknown[] ? Rest : []>
31+
: [First, ...DeduplicateRelationships<Rest extends readonly unknown[] ? Rest : []>]
32+
: T
33+
1734
export type GetFieldNodeResultName<Field extends Ast.FieldNode> = Field['alias'] extends string
1835
? Field['alias']
1936
: Field['aggregateFunction'] extends AggregateFunctions
@@ -87,10 +104,14 @@ type CheckDuplicates<Arr extends any[], Current> = Arr extends [infer Head, ...i
87104
/**
88105
* Iterates over the elements of the array to find duplicates
89106
*/
90-
type FindDuplicates<Arr extends any[]> = Arr extends [infer Head, ...infer Tail]
91-
? CheckDuplicates<Tail, Head> | FindDuplicates<Tail>
107+
type FindDuplicatesWithinDeduplicated<Arr extends any[]> = Arr extends [infer Head, ...infer Tail]
108+
? CheckDuplicates<Tail, Head> | FindDuplicatesWithinDeduplicated<Tail>
92109
: never
93110

111+
type FindDuplicates<Arr extends any[]> = FindDuplicatesWithinDeduplicated<
112+
DeduplicateRelationships<Arr>
113+
>
114+
94115
export type CheckDuplicateEmbededReference<
95116
Schema extends GenericSchema,
96117
RelationName extends string,
@@ -137,17 +158,22 @@ type HasFKeyToFRel<FRelName, Relationships> = Relationships extends [infer R]
137158
/**
138159
* Checks if there is more than one relation to a given foreign relation name in the Relationships.
139160
*/
140-
type HasMultipleFKeysToFRel<FRelName, Relationships> = Relationships extends [
161+
type HasMultipleFKeysToFRelDeduplicated<FRelName, Relationships> = Relationships extends [
141162
infer R,
142163
...infer Rest
143164
]
144165
? R extends { referencedRelation: FRelName }
145166
? HasFKeyToFRel<FRelName, Rest> extends true
146167
? true
147-
: HasMultipleFKeysToFRel<FRelName, Rest>
148-
: HasMultipleFKeysToFRel<FRelName, Rest>
168+
: HasMultipleFKeysToFRelDeduplicated<FRelName, Rest>
169+
: HasMultipleFKeysToFRelDeduplicated<FRelName, Rest>
149170
: false
150171

172+
type HasMultipleFKeysToFRel<
173+
FRelName,
174+
Relationships extends unknown[]
175+
> = HasMultipleFKeysToFRelDeduplicated<FRelName, DeduplicateRelationships<Relationships>>
176+
151177
type CheckRelationshipError<
152178
Schema extends GenericSchema,
153179
Relationships extends GenericRelationship[],
@@ -454,9 +480,13 @@ type ResolveJoinTableRelationship<
454480
CurrentTableOrView extends keyof TablesAndViews<Schema> & string,
455481
FieldName extends string
456482
> = {
457-
[TableName in keyof TablesAndViews<Schema>]: TablesAndViews<Schema>[TableName]['Relationships'] extends readonly (infer Rel)[]
483+
[TableName in keyof TablesAndViews<Schema>]: DeduplicateRelationships<
484+
TablesAndViews<Schema>[TableName]['Relationships']
485+
> extends readonly (infer Rel)[]
458486
? Rel extends { referencedRelation: CurrentTableOrView }
459-
? TablesAndViews<Schema>[TableName]['Relationships'] extends readonly (infer OtherRel)[]
487+
? DeduplicateRelationships<
488+
TablesAndViews<Schema>[TableName]['Relationships']
489+
> extends readonly (infer OtherRel)[]
460490
? OtherRel extends { referencedRelation: FieldName }
461491
? OtherRel
462492
: never
+55
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,55 @@
1+
import { expectType } from 'tsd'
2+
import { TypeEqual } from 'ts-expect'
3+
import { DeduplicateRelationships } from '../../src/select-query-parser/utils'
4+
// Deduplicate exact sames relationships
5+
{
6+
type rels = [
7+
{
8+
foreignKeyName: 'test_fkey'
9+
columns: ['project_id']
10+
referencedRelation: 'project_subscriptions'
11+
referencedColumns: ['project_id']
12+
},
13+
{
14+
foreignKeyName: 'test_fkey'
15+
columns: ['project_id']
16+
referencedRelation: 'projects'
17+
referencedColumns: ['id']
18+
},
19+
{
20+
foreignKeyName: 'test_fkey'
21+
columns: ['project_id']
22+
referencedRelation: 'projects'
23+
referencedColumns: ['id']
24+
},
25+
{
26+
foreignKeyName: 'test_fkey'
27+
columns: ['project_id']
28+
referencedRelation: 'sls_physical_backups_monitoring'
29+
referencedColumns: ['project_id']
30+
}
31+
]
32+
type expected = [
33+
{
34+
foreignKeyName: 'test_fkey'
35+
columns: ['project_id']
36+
referencedRelation: 'project_subscriptions'
37+
referencedColumns: ['project_id']
38+
},
39+
{
40+
foreignKeyName: 'test_fkey'
41+
columns: ['project_id']
42+
referencedRelation: 'projects'
43+
referencedColumns: ['id']
44+
},
45+
{
46+
foreignKeyName: 'test_fkey'
47+
columns: ['project_id']
48+
referencedRelation: 'sls_physical_backups_monitoring'
49+
referencedColumns: ['project_id']
50+
}
51+
]
52+
53+
type result = DeduplicateRelationships<rels>
54+
expectType<TypeEqual<result, expected>>(true)
55+
}

test/types.ts

+70
Original file line numberDiff line numberDiff line change
@@ -86,6 +86,13 @@ export type Database = {
8686
referencedRelation: 'users'
8787
referencedColumns: ['username']
8888
},
89+
{
90+
foreignKeyName: 'best_friends_first_user_fkey'
91+
columns: ['first_user']
92+
isOneToOne: false
93+
referencedRelation: 'users'
94+
referencedColumns: ['username']
95+
},
8996
{
9097
foreignKeyName: 'best_friends_second_user_fkey'
9198
columns: ['second_user']
@@ -107,6 +114,13 @@ export type Database = {
107114
referencedRelation: 'users'
108115
referencedColumns: ['username']
109116
},
117+
{
118+
foreignKeyName: 'best_friends_second_user_fkey'
119+
columns: ['second_user']
120+
isOneToOne: false
121+
referencedRelation: 'users'
122+
referencedColumns: ['username']
123+
},
110124
{
111125
foreignKeyName: 'best_friends_third_wheel_fkey'
112126
columns: ['third_wheel']
@@ -121,6 +135,13 @@ export type Database = {
121135
referencedRelation: 'updatable_view'
122136
referencedColumns: ['username']
123137
},
138+
{
139+
foreignKeyName: 'best_friends_third_wheel_fkey'
140+
columns: ['third_wheel']
141+
isOneToOne: false
142+
referencedRelation: 'users'
143+
referencedColumns: ['username']
144+
},
124145
{
125146
foreignKeyName: 'best_friends_third_wheel_fkey'
126147
columns: ['third_wheel']
@@ -144,6 +165,13 @@ export type Database = {
144165
id?: number
145166
}
146167
Relationships: [
168+
{
169+
foreignKeyName: 'channel_details_id_fkey'
170+
columns: ['id']
171+
isOneToOne: true
172+
referencedRelation: 'channels'
173+
referencedColumns: ['id']
174+
},
147175
{
148176
foreignKeyName: 'channel_details_id_fkey'
149177
columns: ['id']
@@ -188,6 +216,13 @@ export type Database = {
188216
parent_id?: number | null
189217
}
190218
Relationships: [
219+
{
220+
foreignKeyName: 'collections_parent_id_fkey'
221+
columns: ['parent_id']
222+
isOneToOne: false
223+
referencedRelation: 'collections'
224+
referencedColumns: ['id']
225+
},
191226
{
192227
foreignKeyName: 'collections_parent_id_fkey'
193228
columns: ['parent_id']
@@ -220,6 +255,13 @@ export type Database = {
220255
username?: string
221256
}
222257
Relationships: [
258+
{
259+
foreignKeyName: 'messages_channel_id_fkey'
260+
columns: ['channel_id']
261+
isOneToOne: false
262+
referencedRelation: 'channels'
263+
referencedColumns: ['id']
264+
},
223265
{
224266
foreignKeyName: 'messages_channel_id_fkey'
225267
columns: ['channel_id']
@@ -241,6 +283,13 @@ export type Database = {
241283
referencedRelation: 'updatable_view'
242284
referencedColumns: ['username']
243285
},
286+
{
287+
foreignKeyName: 'messages_username_fkey'
288+
columns: ['username']
289+
isOneToOne: false
290+
referencedRelation: 'users'
291+
referencedColumns: ['username']
292+
},
244293
{
245294
foreignKeyName: 'messages_username_fkey'
246295
columns: ['username']
@@ -264,6 +313,20 @@ export type Database = {
264313
product_id?: number
265314
}
266315
Relationships: [
316+
{
317+
foreignKeyName: 'product_categories_category_id_fkey'
318+
columns: ['category_id']
319+
isOneToOne: false
320+
referencedRelation: 'categories'
321+
referencedColumns: ['id']
322+
},
323+
{
324+
foreignKeyName: 'product_categories_product_id_fkey'
325+
columns: ['product_id']
326+
isOneToOne: false
327+
referencedRelation: 'products'
328+
referencedColumns: ['id']
329+
},
267330
{
268331
foreignKeyName: 'product_categories_category_id_fkey'
269332
columns: ['category_id']
@@ -389,6 +452,13 @@ export type Database = {
389452
referencedRelation: 'updatable_view'
390453
referencedColumns: ['username']
391454
},
455+
{
456+
foreignKeyName: 'user_profiles_username_fkey'
457+
columns: ['username']
458+
isOneToOne: false
459+
referencedRelation: 'users'
460+
referencedColumns: ['username']
461+
},
392462
{
393463
foreignKeyName: 'user_profiles_username_fkey'
394464
columns: ['username']

0 commit comments

Comments
 (0)