Skip to content

Commit 2928692

Browse files
committed
Handle column name embed
1 parent 6f5d570 commit 2928692

File tree

2 files changed

+100
-60
lines changed

2 files changed

+100
-60
lines changed

src/select-query-parser.ts

+76-60
Original file line numberDiff line numberDiff line change
@@ -129,54 +129,91 @@ type EatWhitespace<Input extends string> = string extends Input
129129
: Input
130130

131131
/**
132-
* Returns a boolean representing whether there is a foreign key with the given name.
132+
* Check if Item is in Array
133133
*/
134-
type HasFKey<FKeyName, Relationships> = Relationships extends [infer R]
135-
? R extends { foreignKeyName: FKeyName }
134+
type InArray<Item, Array> = Array extends [infer I]
135+
? I extends Item
136136
? true
137137
: false
138-
: Relationships extends [infer R, ...infer Rest]
139-
? HasFKey<FKeyName, [R]> extends true
138+
: Array extends [infer I, ...infer Rest]
139+
? InArray<Item, [I]> extends true
140140
? true
141-
: HasFKey<FKeyName, Rest>
141+
: InArray<Item, Rest>
142142
: false
143143

144+
/**
145+
* Returns a boolean representing whether there is a foreign key with the given name.
146+
*/
147+
type HasFKey<FKeyName, Relationships> = InArray<{ foreignKeyName: FKeyName }, Relationships>
144148
/**
145149
* Returns a boolean representing whether there the foreign key has a unique constraint.
146150
*/
147-
type HasUniqueFKey<FKeyName, Relationships> = Relationships extends [infer R]
148-
? R extends { foreignKeyName: FKeyName; isOneToOne: true }
149-
? true
150-
: false
151+
type HasUniqueFKey<FKeyName, Relationships> = InArray<
152+
{ foreignKeyName: FKeyName; isOneToOne: true },
153+
Relationships
154+
>
155+
156+
/**
157+
* Returns a boolean representing whether there is a column that references a relation
158+
*/
159+
type ColumnForeignRelation<ColName, Relationships> = Relationships extends [infer R]
160+
? R extends { columns: string[]; referencedRelation: string }
161+
? InArray<ColName, R['columns']> extends true
162+
? [R['referencedRelation']]
163+
: null
164+
: null
151165
: Relationships extends [infer R, ...infer Rest]
152-
? HasUniqueFKey<FKeyName, [R]> extends true
153-
? true
154-
: HasUniqueFKey<FKeyName, Rest>
155-
: false
166+
? ColumnForeignRelation<ColName, [R]> extends [infer Rel]
167+
? [Rel]
168+
: ColumnForeignRelation<ColName, Rest>
169+
: null
156170

157171
/**
158172
* Returns a boolean representing whether there is a foreign key referencing
159173
* a given relation.
160174
*/
161-
type HasFKeyToFRel<FRelName, Relationships> = Relationships extends [infer R]
162-
? R extends { referencedRelation: FRelName }
163-
? true
164-
: false
165-
: Relationships extends [infer R, ...infer Rest]
166-
? HasFKeyToFRel<FRelName, [R]> extends true
167-
? true
168-
: HasFKeyToFRel<FRelName, Rest>
169-
: false
175+
type HasFKeyToFRel<FRelName, Relationships> = InArray<
176+
{ referencedRelation: FRelName },
177+
Relationships
178+
>
170179

171-
type HasUniqueFKeyToFRel<FRelName, Relationships> = Relationships extends [infer R]
172-
? R extends { referencedRelation: FRelName; isOneToOne: true }
173-
? true
174-
: false
175-
: Relationships extends [infer R, ...infer Rest]
176-
? HasUniqueFKeyToFRel<FRelName, [R]> extends true
177-
? true
178-
: HasUniqueFKeyToFRel<FRelName, Rest>
179-
: false
180+
type HasUniqueFKeyToFRel<FRelName, Relationships> = InArray<
181+
{ referencedRelation: FRelName; isOneToOne: true },
182+
Relationships
183+
>
184+
185+
type FieldRelation<
186+
Schema extends GenericSchema,
187+
Field extends { name: string; children: unknown[] },
188+
FRel extends keyof (Schema['Tables'] & Schema['Views']),
189+
RelationName,
190+
Relationships
191+
> = {
192+
[_ in Field['name']]: GetResultHelper<
193+
Schema,
194+
(Schema['Tables'] & Schema['Views'])[FRel]['Row'],
195+
FRel,
196+
(Schema['Tables'] & Schema['Views'])[FRel] extends { Relationships: infer R } ? R : unknown,
197+
Field['children'],
198+
unknown
199+
> extends infer Child
200+
? // One-to-one relationship - referencing column(s) has unique/pkey constraint.
201+
HasUniqueFKeyToFRel<
202+
RelationName,
203+
(Schema['Tables'] & Schema['Views'])[FRel] extends {
204+
Relationships: infer R
205+
}
206+
? R
207+
: unknown
208+
> extends true
209+
? Child | null
210+
: Relationships extends unknown[]
211+
? HasFKeyToFRel<FRel, Relationships> extends true
212+
? Child | null
213+
: Child[]
214+
: Child[]
215+
: never
216+
}
180217

181218
/**
182219
* Constructs a type definition for a single field of an object.
@@ -237,34 +274,13 @@ type ConstructFieldDefinition<
237274
: never
238275
}
239276
: Field extends { name: string; original: string; children: unknown[] }
240-
? {
241-
[_ in Field['name']]: GetResultHelper<
242-
Schema,
243-
(Schema['Tables'] & Schema['Views'])[Field['original']]['Row'],
244-
Field['original'],
245-
(Schema['Tables'] & Schema['Views'])[Field['original']] extends { Relationships: infer R }
246-
? R
247-
: unknown,
248-
Field['children'],
249-
unknown
250-
> extends infer Child
251-
? // One-to-one relationship - referencing column(s) has unique/pkey constraint.
252-
HasUniqueFKeyToFRel<
253-
RelationName,
254-
(Schema['Tables'] & Schema['Views'])[Field['original']] extends {
255-
Relationships: infer R
256-
}
257-
? R
258-
: unknown
259-
> extends true
260-
? Child | null
261-
: Relationships extends unknown[]
262-
? HasFKeyToFRel<Field['original'], Relationships> extends true
263-
? Child | null
264-
: Child[]
265-
: Child[]
266-
: never
267-
}
277+
? ColumnForeignRelation<Field['original'], Relationships> extends [infer ForeignRel]
278+
? // handle `col:foreign_key_column`
279+
ForeignRel extends keyof (Schema['Tables'] & Schema['Views'])
280+
? FieldRelation<Schema, Field, ForeignRel, RelationName, Relationships>
281+
: SelectQueryError<`Unknown relation in a relationship`>
282+
: // handle `col:relation`
283+
FieldRelation<Schema, Field, Field['original'], RelationName, Relationships>
268284
: Field extends { name: string; type: infer T }
269285
? { [K in Field['name']]: T }
270286
: Field extends { name: string; original: string }

test/index.test-d.ts

+24
Original file line numberDiff line numberDiff line change
@@ -149,6 +149,30 @@ const postgrest = new PostgrestClient<Database>(REST_URL)
149149
expectType<Database['public']['Tables']['users']['Row'] | null>(message.user)
150150
}
151151

152+
// many-to-one relationship (fkey)
153+
{
154+
const { data: message, error } = await postgrest
155+
.from('messages')
156+
.select('user:users!messages_username_fkey(*)')
157+
.single()
158+
if (error) {
159+
throw new Error(error.message)
160+
}
161+
expectType<Database['public']['Tables']['users']['Row'] | null>(message.user)
162+
}
163+
164+
// many-to-one relationship (column name)
165+
{
166+
const { data: message, error } = await postgrest
167+
.from('messages')
168+
.select('user:username(*)')
169+
.single()
170+
if (error) {
171+
throw new Error(error.message)
172+
}
173+
expectType<Database['public']['Tables']['users']['Row'] | null>(message.user)
174+
}
175+
152176
// one-to-many relationship
153177
{
154178
const { data: user, error } = await postgrest.from('users').select('messages(*)').single()

0 commit comments

Comments
 (0)