Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Handle column name embed #451

Open
wants to merge 2 commits into
base: master
Choose a base branch
from
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
136 changes: 76 additions & 60 deletions src/select-query-parser.ts
Original file line number Diff line number Diff line change
Expand Up @@ -129,54 +129,91 @@ type EatWhitespace<Input extends string> = string extends Input
: Input

/**
* Returns a boolean representing whether there is a foreign key with the given name.
* Check if Item is in Array
*/
type HasFKey<FKeyName, Relationships> = Relationships extends [infer R]
? R extends { foreignKeyName: FKeyName }
type InArray<Item, Array> = Array extends [infer I]
? I extends Item
? true
: false
: Relationships extends [infer R, ...infer Rest]
? HasFKey<FKeyName, [R]> extends true
: Array extends [infer I, ...infer Rest]
? InArray<Item, [I]> extends true
? true
: HasFKey<FKeyName, Rest>
: InArray<Item, Rest>
: false

/**
* Returns a boolean representing whether there is a foreign key with the given name.
*/
type HasFKey<FKeyName, Relationships> = InArray<{ foreignKeyName: FKeyName }, Relationships>
/**
* Returns a boolean representing whether there the foreign key has a unique constraint.
*/
type HasUniqueFKey<FKeyName, Relationships> = Relationships extends [infer R]
? R extends { foreignKeyName: FKeyName; isOneToOne: true }
? true
: false
type HasUniqueFKey<FKeyName, Relationships> = InArray<
{ foreignKeyName: FKeyName; isOneToOne: true },
Relationships
>

/**
* Returns the relation referenced by this column name, if such relation exists
*/
type ColumnForeignRelation<ColName, Relationships> = Relationships extends [infer R]
? R extends { columns: string[]; referencedRelation: string }
? InArray<ColName, R['columns']> extends true
? [R['referencedRelation']]
: null
: null
: Relationships extends [infer R, ...infer Rest]
? HasUniqueFKey<FKeyName, [R]> extends true
? true
: HasUniqueFKey<FKeyName, Rest>
: false
? ColumnForeignRelation<ColName, [R]> extends [infer Rel]
? [Rel]
: ColumnForeignRelation<ColName, Rest>
: null

/**
* Returns a boolean representing whether there is a foreign key referencing
* a given relation.
*/
type HasFKeyToFRel<FRelName, Relationships> = Relationships extends [infer R]
? R extends { referencedRelation: FRelName }
? true
: false
: Relationships extends [infer R, ...infer Rest]
? HasFKeyToFRel<FRelName, [R]> extends true
? true
: HasFKeyToFRel<FRelName, Rest>
: false
type HasFKeyToFRel<FRelName, Relationships> = InArray<
{ referencedRelation: FRelName },
Relationships
>

type HasUniqueFKeyToFRel<FRelName, Relationships> = Relationships extends [infer R]
? R extends { referencedRelation: FRelName; isOneToOne: true }
? true
: false
: Relationships extends [infer R, ...infer Rest]
? HasUniqueFKeyToFRel<FRelName, [R]> extends true
? true
: HasUniqueFKeyToFRel<FRelName, Rest>
: false
type HasUniqueFKeyToFRel<FRelName, Relationships> = InArray<
{ referencedRelation: FRelName; isOneToOne: true },
Relationships
>

type FieldRelation<
Schema extends GenericSchema,
Field extends { name: string; children: unknown[] },
FRel extends keyof (Schema['Tables'] & Schema['Views']),
RelationName,
Relationships
> = {
[_ in Field['name']]: GetResultHelper<
Schema,
(Schema['Tables'] & Schema['Views'])[FRel]['Row'],
FRel,
(Schema['Tables'] & Schema['Views'])[FRel] extends { Relationships: infer R } ? R : unknown,
Field['children'],
unknown
> extends infer Child
? // One-to-one relationship - referencing column(s) has unique/pkey constraint.
HasUniqueFKeyToFRel<
RelationName,
(Schema['Tables'] & Schema['Views'])[FRel] extends {
Relationships: infer R
}
? R
: unknown
> extends true
? Child | null
: Relationships extends unknown[]
? HasFKeyToFRel<FRel, Relationships> extends true
? Child | null
: Child[]
: Child[]
: never
}

/**
* Constructs a type definition for a single field of an object.
Expand Down Expand Up @@ -237,34 +274,13 @@ type ConstructFieldDefinition<
: never
}
: Field extends { name: string; original: string; children: unknown[] }
? {
[_ in Field['name']]: GetResultHelper<
Schema,
(Schema['Tables'] & Schema['Views'])[Field['original']]['Row'],
Field['original'],
(Schema['Tables'] & Schema['Views'])[Field['original']] extends { Relationships: infer R }
? R
: unknown,
Field['children'],
unknown
> extends infer Child
? // One-to-one relationship - referencing column(s) has unique/pkey constraint.
HasUniqueFKeyToFRel<
RelationName,
(Schema['Tables'] & Schema['Views'])[Field['original']] extends {
Relationships: infer R
}
? R
: unknown
> extends true
? Child | null
: Relationships extends unknown[]
? HasFKeyToFRel<Field['original'], Relationships> extends true
? Child | null
: Child[]
: Child[]
: never
}
? ColumnForeignRelation<Field['original'], Relationships> extends [infer ForeignRel]
? // handle `col:foreign_key_column`
ForeignRel extends keyof (Schema['Tables'] & Schema['Views'])
? FieldRelation<Schema, Field, ForeignRel, RelationName, Relationships>
: SelectQueryError<`Unknown relation in a relationship`>
: // handle `col:relation`
FieldRelation<Schema, Field, Field['original'], RelationName, Relationships>
: Field extends { name: string; type: infer T }
? { [K in Field['name']]: T }
: Field extends { name: string; original: string }
Expand Down
24 changes: 24 additions & 0 deletions test/index.test-d.ts
Original file line number Diff line number Diff line change
Expand Up @@ -149,6 +149,30 @@ const postgrest = new PostgrestClient<Database>(REST_URL)
expectType<Database['public']['Tables']['users']['Row'] | null>(message.user)
}

// many-to-one relationship (fkey)
{
const { data: message, error } = await postgrest
.from('messages')
.select('user:users!messages_username_fkey(*)')
.single()
if (error) {
throw new Error(error.message)
}
expectType<Database['public']['Tables']['users']['Row'] | null>(message.user)
}

// many-to-one relationship (column name)
{
const { data: message, error } = await postgrest
.from('messages')
.select('user:username(*)')
.single()
if (error) {
throw new Error(error.message)
}
expectType<Database['public']['Tables']['users']['Row'] | null>(message.user)
}

// one-to-many relationship
{
const { data: user, error } = await postgrest.from('users').select('messages(*)').single()
Expand Down
Loading