diff --git a/src/select-query-parser/utils.ts b/src/select-query-parser/utils.ts index 1b1a3bea..b3691e93 100644 --- a/src/select-query-parser/utils.ts +++ b/src/select-query-parser/utils.ts @@ -14,6 +14,23 @@ export type IsAny = 0 extends 1 & T ? true : false export type SelectQueryError = { error: true } & Message +/* + ** Because of pg-meta types generation there is some cases where a same relationship can be duplicated + ** if the relation is across schemas and views this ensure that we dedup those relations and treat them + ** as postgrest would. + ** This is no longer the case and has been patched here: https://github.com/supabase/postgres-meta/pull/809 + ** But we still need this for retro-compatibilty with older generated types + ** TODO: Remove this in next major version + */ +export type DeduplicateRelationships = T extends readonly [ + infer First, + ...infer Rest +] + ? First extends Rest[number] + ? DeduplicateRelationships + : [First, ...DeduplicateRelationships] + : T + export type GetFieldNodeResultName = Field['alias'] extends string ? Field['alias'] : Field['aggregateFunction'] extends AggregateFunctions @@ -87,10 +104,14 @@ type CheckDuplicates = Arr extends [infer Head, ...i /** * Iterates over the elements of the array to find duplicates */ -type FindDuplicates = Arr extends [infer Head, ...infer Tail] - ? CheckDuplicates | FindDuplicates +type FindDuplicatesWithinDeduplicated = Arr extends [infer Head, ...infer Tail] + ? CheckDuplicates | FindDuplicatesWithinDeduplicated : never +type FindDuplicates = FindDuplicatesWithinDeduplicated< + DeduplicateRelationships +> + export type CheckDuplicateEmbededReference< Schema extends GenericSchema, RelationName extends string, @@ -137,17 +158,22 @@ type HasFKeyToFRel = Relationships extends [infer R] /** * Checks if there is more than one relation to a given foreign relation name in the Relationships. */ -type HasMultipleFKeysToFRel = Relationships extends [ +type HasMultipleFKeysToFRelDeduplicated = Relationships extends [ infer R, ...infer Rest ] ? R extends { referencedRelation: FRelName } ? HasFKeyToFRel extends true ? true - : HasMultipleFKeysToFRel - : HasMultipleFKeysToFRel + : HasMultipleFKeysToFRelDeduplicated + : HasMultipleFKeysToFRelDeduplicated : false +type HasMultipleFKeysToFRel< + FRelName, + Relationships extends unknown[] +> = HasMultipleFKeysToFRelDeduplicated> + type CheckRelationshipError< Schema extends GenericSchema, Relationships extends GenericRelationship[], @@ -454,9 +480,13 @@ type ResolveJoinTableRelationship< CurrentTableOrView extends keyof TablesAndViews & string, FieldName extends string > = { - [TableName in keyof TablesAndViews]: TablesAndViews[TableName]['Relationships'] extends readonly (infer Rel)[] + [TableName in keyof TablesAndViews]: DeduplicateRelationships< + TablesAndViews[TableName]['Relationships'] + > extends readonly (infer Rel)[] ? Rel extends { referencedRelation: CurrentTableOrView } - ? TablesAndViews[TableName]['Relationships'] extends readonly (infer OtherRel)[] + ? DeduplicateRelationships< + TablesAndViews[TableName]['Relationships'] + > extends readonly (infer OtherRel)[] ? OtherRel extends { referencedRelation: FieldName } ? OtherRel : never diff --git a/test/select-query-parser/types.test-d.ts b/test/select-query-parser/types.test-d.ts new file mode 100644 index 00000000..d52432df --- /dev/null +++ b/test/select-query-parser/types.test-d.ts @@ -0,0 +1,55 @@ +import { expectType } from 'tsd' +import { TypeEqual } from 'ts-expect' +import { DeduplicateRelationships } from '../../src/select-query-parser/utils' +// Deduplicate exact sames relationships +{ + type rels = [ + { + foreignKeyName: 'test_fkey' + columns: ['project_id'] + referencedRelation: 'project_subscriptions' + referencedColumns: ['project_id'] + }, + { + foreignKeyName: 'test_fkey' + columns: ['project_id'] + referencedRelation: 'projects' + referencedColumns: ['id'] + }, + { + foreignKeyName: 'test_fkey' + columns: ['project_id'] + referencedRelation: 'projects' + referencedColumns: ['id'] + }, + { + foreignKeyName: 'test_fkey' + columns: ['project_id'] + referencedRelation: 'sls_physical_backups_monitoring' + referencedColumns: ['project_id'] + } + ] + type expected = [ + { + foreignKeyName: 'test_fkey' + columns: ['project_id'] + referencedRelation: 'project_subscriptions' + referencedColumns: ['project_id'] + }, + { + foreignKeyName: 'test_fkey' + columns: ['project_id'] + referencedRelation: 'projects' + referencedColumns: ['id'] + }, + { + foreignKeyName: 'test_fkey' + columns: ['project_id'] + referencedRelation: 'sls_physical_backups_monitoring' + referencedColumns: ['project_id'] + } + ] + + type result = DeduplicateRelationships + expectType>(true) +} diff --git a/test/types.ts b/test/types.ts index bd26dd1e..2ed1d1c4 100644 --- a/test/types.ts +++ b/test/types.ts @@ -86,6 +86,13 @@ export type Database = { referencedRelation: 'users' referencedColumns: ['username'] }, + { + foreignKeyName: 'best_friends_first_user_fkey' + columns: ['first_user'] + isOneToOne: false + referencedRelation: 'users' + referencedColumns: ['username'] + }, { foreignKeyName: 'best_friends_second_user_fkey' columns: ['second_user'] @@ -107,6 +114,13 @@ export type Database = { referencedRelation: 'users' referencedColumns: ['username'] }, + { + foreignKeyName: 'best_friends_second_user_fkey' + columns: ['second_user'] + isOneToOne: false + referencedRelation: 'users' + referencedColumns: ['username'] + }, { foreignKeyName: 'best_friends_third_wheel_fkey' columns: ['third_wheel'] @@ -121,6 +135,13 @@ export type Database = { referencedRelation: 'updatable_view' referencedColumns: ['username'] }, + { + foreignKeyName: 'best_friends_third_wheel_fkey' + columns: ['third_wheel'] + isOneToOne: false + referencedRelation: 'users' + referencedColumns: ['username'] + }, { foreignKeyName: 'best_friends_third_wheel_fkey' columns: ['third_wheel'] @@ -144,6 +165,13 @@ export type Database = { id?: number } Relationships: [ + { + foreignKeyName: 'channel_details_id_fkey' + columns: ['id'] + isOneToOne: true + referencedRelation: 'channels' + referencedColumns: ['id'] + }, { foreignKeyName: 'channel_details_id_fkey' columns: ['id'] @@ -188,6 +216,13 @@ export type Database = { parent_id?: number | null } Relationships: [ + { + foreignKeyName: 'collections_parent_id_fkey' + columns: ['parent_id'] + isOneToOne: false + referencedRelation: 'collections' + referencedColumns: ['id'] + }, { foreignKeyName: 'collections_parent_id_fkey' columns: ['parent_id'] @@ -220,6 +255,13 @@ export type Database = { username?: string } Relationships: [ + { + foreignKeyName: 'messages_channel_id_fkey' + columns: ['channel_id'] + isOneToOne: false + referencedRelation: 'channels' + referencedColumns: ['id'] + }, { foreignKeyName: 'messages_channel_id_fkey' columns: ['channel_id'] @@ -241,6 +283,13 @@ export type Database = { referencedRelation: 'updatable_view' referencedColumns: ['username'] }, + { + foreignKeyName: 'messages_username_fkey' + columns: ['username'] + isOneToOne: false + referencedRelation: 'users' + referencedColumns: ['username'] + }, { foreignKeyName: 'messages_username_fkey' columns: ['username'] @@ -264,6 +313,20 @@ export type Database = { product_id?: number } Relationships: [ + { + foreignKeyName: 'product_categories_category_id_fkey' + columns: ['category_id'] + isOneToOne: false + referencedRelation: 'categories' + referencedColumns: ['id'] + }, + { + foreignKeyName: 'product_categories_product_id_fkey' + columns: ['product_id'] + isOneToOne: false + referencedRelation: 'products' + referencedColumns: ['id'] + }, { foreignKeyName: 'product_categories_category_id_fkey' columns: ['category_id'] @@ -389,6 +452,13 @@ export type Database = { referencedRelation: 'updatable_view' referencedColumns: ['username'] }, + { + foreignKeyName: 'user_profiles_username_fkey' + columns: ['username'] + isOneToOne: false + referencedRelation: 'users' + referencedColumns: ['username'] + }, { foreignKeyName: 'user_profiles_username_fkey' columns: ['username']