Skip to content

Commit

Permalink
feat: infer return type of functions returning table row type
Browse files Browse the repository at this point in the history
  • Loading branch information
kakeluh authored Feb 13, 2025
1 parent ca06061 commit 8418eaa
Show file tree
Hide file tree
Showing 7 changed files with 94 additions and 29 deletions.
17 changes: 16 additions & 1 deletion src/lib/PostgresMetaTypes.ts
Original file line number Diff line number Diff line change
Expand Up @@ -11,21 +11,36 @@ export default class PostgresMetaTypes {
}

async list({
includeTableTypes = false,
includeArrayTypes = false,
includeSystemSchemas = false,
includedSchemas,
excludedSchemas,
limit,
offset,
}: {
includeTableTypes?: boolean
includeArrayTypes?: boolean
includeSystemSchemas?: boolean
includedSchemas?: string[]
excludedSchemas?: string[]
limit?: number
offset?: number
} = {}): Promise<PostgresMetaResult<PostgresType[]>> {
let sql = typesSql
let sql = `${typesSql}
where
(
t.typrelid = 0
or (
select
c.relkind ${includeTableTypes ? `in ('c', 'r')` : `= 'c'`}
from
pg_class c
where
c.oid = t.typrelid
)
)
`
if (!includeArrayTypes) {
sql += ` and not exists (
select
Expand Down
3 changes: 2 additions & 1 deletion src/lib/generators.ts
Original file line number Diff line number Diff line change
Expand Up @@ -4,13 +4,13 @@ import {
PostgresForeignTable,
PostgresFunction,
PostgresMaterializedView,
PostgresMetaResult,
PostgresRelationship,
PostgresSchema,
PostgresTable,
PostgresType,
PostgresView,
} from './types.js'
import { PostgresMetaResult } from './types.js'

export type GeneratorMetadata = {
schemas: PostgresSchema[]
Expand Down Expand Up @@ -98,6 +98,7 @@ export async function getGeneratorMetadata(
}

const { data: types, error: typesError } = await pgMeta.types.list({
includeTableTypes: true,
includeArrayTypes: true,
includeSystemSchemas: true,
})
Expand Down
12 changes: 0 additions & 12 deletions src/lib/sql/types.sql
Original file line number Diff line number Diff line change
Expand Up @@ -33,15 +33,3 @@ from
group by
c.oid
) as t_attributes on t_attributes.oid = t.typrelid
where
(
t.typrelid = 0
or (
select
c.relkind = 'c'
from
pg_class c
where
c.oid = t.typrelid
)
)
1 change: 1 addition & 0 deletions src/server/server.ts
Original file line number Diff line number Diff line change
Expand Up @@ -77,6 +77,7 @@ async function getTypeOutput(): Promise<string | null> {
GENERATE_TYPES_INCLUDED_SCHEMAS.length > 0 ? GENERATE_TYPES_INCLUDED_SCHEMAS : undefined,
}),
pgMeta.types.list({
includeTableTypes: true,
includeArrayTypes: true,
includeSystemSchemas: true,
}),
Expand Down
4 changes: 4 additions & 0 deletions test/db/00-init.sql
Original file line number Diff line number Diff line change
Expand Up @@ -137,3 +137,7 @@ create table table_with_primary_key_other_than_id (
other_id bigint GENERATED BY DEFAULT AS IDENTITY PRIMARY KEY,
name text
);

create type composite_type_with_record_attribute as (
todo todos
);
31 changes: 31 additions & 0 deletions test/lib/types.ts
Original file line number Diff line number Diff line change
Expand Up @@ -57,6 +57,37 @@ test('list types with excluded schemas and include System Schemas', async () =>
})
})

test('list types with include Table Types', async () => {
const res = await pgMeta.types.list({
includeTableTypes: true,
})

expect(res.data?.find(({ name }) => name === 'todos')).toMatchInlineSnapshot(
{ id: expect.any(Number) },
`
{
"attributes": [],
"comment": null,
"enums": [],
"format": "todos",
"id": Any<Number>,
"name": "todos",
"schema": "public",
}
`
)
})

test('list types without Table Types', async () => {
const res = await pgMeta.types.list({
includeTableTypes: false,
})

res.data?.forEach((type) => {
expect(type.name).not.toBe('todos')
})
})

test('composite type attributes', async () => {
await pgMeta.query(`create type test_composite as (id int8, data text);`)

Expand Down
55 changes: 40 additions & 15 deletions test/server/typegen.ts
Original file line number Diff line number Diff line change
Expand Up @@ -333,31 +333,31 @@ test('typegen: typescript', async () => {
Functions: {
blurb: {
Args: {
"": unknown
"": Database["public"]["Tables"]["todos"]["Row"]
}
Returns: string
}
blurb_varchar: {
Args: {
"": unknown
"": Database["public"]["Tables"]["todos"]["Row"]
}
Returns: string
}
details_is_long: {
Args: {
"": unknown
"": Database["public"]["Tables"]["todos"]["Row"]
}
Returns: boolean
}
details_length: {
Args: {
"": unknown
"": Database["public"]["Tables"]["todos"]["Row"]
}
Returns: number
}
details_words: {
Args: {
"": unknown
"": Database["public"]["Tables"]["todos"]["Row"]
}
Returns: string[]
}
Expand Down Expand Up @@ -424,6 +424,9 @@ test('typegen: typescript', async () => {
composite_type_with_array_attribute: {
my_text_array: string[] | null
}
composite_type_with_record_attribute: {
todo: Database["public"]["Tables"]["todos"]["Row"] | null
}
}
}
}
Expand Down Expand Up @@ -877,31 +880,31 @@ test('typegen w/ one-to-one relationships', async () => {
Functions: {
blurb: {
Args: {
"": unknown
"": Database["public"]["Tables"]["todos"]["Row"]
}
Returns: string
}
blurb_varchar: {
Args: {
"": unknown
"": Database["public"]["Tables"]["todos"]["Row"]
}
Returns: string
}
details_is_long: {
Args: {
"": unknown
"": Database["public"]["Tables"]["todos"]["Row"]
}
Returns: boolean
}
details_length: {
Args: {
"": unknown
"": Database["public"]["Tables"]["todos"]["Row"]
}
Returns: number
}
details_words: {
Args: {
"": unknown
"": Database["public"]["Tables"]["todos"]["Row"]
}
Returns: string[]
}
Expand Down Expand Up @@ -968,6 +971,9 @@ test('typegen w/ one-to-one relationships', async () => {
composite_type_with_array_attribute: {
my_text_array: string[] | null
}
composite_type_with_record_attribute: {
todo: Database["public"]["Tables"]["todos"]["Row"] | null
}
}
}
}
Expand Down Expand Up @@ -1421,31 +1427,31 @@ test('typegen: typescript w/ one-to-one relationships', async () => {
Functions: {
blurb: {
Args: {
"": unknown
"": Database["public"]["Tables"]["todos"]["Row"]
}
Returns: string
}
blurb_varchar: {
Args: {
"": unknown
"": Database["public"]["Tables"]["todos"]["Row"]
}
Returns: string
}
details_is_long: {
Args: {
"": unknown
"": Database["public"]["Tables"]["todos"]["Row"]
}
Returns: boolean
}
details_length: {
Args: {
"": unknown
"": Database["public"]["Tables"]["todos"]["Row"]
}
Returns: number
}
details_words: {
Args: {
"": unknown
"": Database["public"]["Tables"]["todos"]["Row"]
}
Returns: string[]
}
Expand Down Expand Up @@ -1512,6 +1518,9 @@ test('typegen: typescript w/ one-to-one relationships', async () => {
composite_type_with_array_attribute: {
my_text_array: string[] | null
}
composite_type_with_record_attribute: {
todo: Database["public"]["Tables"]["todos"]["Row"] | null
}
}
}
}
Expand Down Expand Up @@ -1803,6 +1812,10 @@ test('typegen: go', async () => {
type PublicCompositeTypeWithArrayAttribute struct {
MyTextArray interface{} \`json:"my_text_array"\`
}
type PublicCompositeTypeWithRecordAttribute struct {
Todo interface{} \`json:"todo"\`
}"
`)
})
Expand Down Expand Up @@ -2144,6 +2157,12 @@ test('typegen: swift', async () => {
case MyTextArray = "my_text_array"
}
}
internal struct CompositeTypeWithRecordAttribute: Codable, Hashable, Sendable {
internal let Todo: TodosSelect
internal enum CodingKeys: String, CodingKey {
case Todo = "todo"
}
}
}"
`)
})
Expand Down Expand Up @@ -2489,6 +2508,12 @@ test('typegen: swift w/ public access control', async () => {
case MyTextArray = "my_text_array"
}
}
public struct CompositeTypeWithRecordAttribute: Codable, Hashable, Sendable {
public let Todo: TodosSelect
public enum CodingKeys: String, CodingKey {
case Todo = "todo"
}
}
}"
`)
})

0 comments on commit 8418eaa

Please sign in to comment.