Skip to content

Commit 96f184c

Browse files
committed
feat(functions): setup typegen for embeded function type inferences
1 parent 6af5d63 commit 96f184c

File tree

5 files changed

+305
-236
lines changed

5 files changed

+305
-236
lines changed

Diff for: src/lib/sql/functions.sql

+36-20
Original file line numberDiff line numberDiff line change
@@ -90,32 +90,48 @@ from
9090
select
9191
oid,
9292
jsonb_agg(jsonb_build_object(
93-
'mode', t2.mode,
93+
'mode', mode,
9494
'name', name,
9595
'type_id', type_id,
96-
'has_default', has_default
96+
'has_default', has_default,
97+
'table_name', table_name
9798
)) as args
9899
from
99100
(
100101
select
101-
oid,
102-
unnest(arg_modes) as mode,
103-
unnest(arg_names) as name,
104-
unnest(arg_types)::int8 as type_id,
105-
unnest(arg_has_defaults) as has_default
102+
t1.oid,
103+
t2.mode,
104+
t1.name,
105+
t1.type_id,
106+
t1.has_default,
107+
case
108+
when pt.typrelid != 0 then pc.relname
109+
else null
110+
end as table_name
106111
from
107-
functions
108-
) as t1,
109-
lateral (
110-
select
111-
case
112-
when t1.mode = 'i' then 'in'
113-
when t1.mode = 'o' then 'out'
114-
when t1.mode = 'b' then 'inout'
115-
when t1.mode = 'v' then 'variadic'
116-
else 'table'
117-
end as mode
118-
) as t2
112+
(
113+
select
114+
oid,
115+
unnest(arg_modes) as mode,
116+
unnest(arg_names) as name,
117+
unnest(arg_types)::int8 as type_id,
118+
unnest(arg_has_defaults) as has_default
119+
from
120+
functions
121+
) as t1
122+
cross join lateral (
123+
select
124+
case
125+
when t1.mode = 'i' then 'in'
126+
when t1.mode = 'o' then 'out'
127+
when t1.mode = 'b' then 'inout'
128+
when t1.mode = 'v' then 'variadic'
129+
else 'table'
130+
end as mode
131+
) as t2
132+
left join pg_type pt on pt.oid = t1.type_id
133+
left join pg_class pc on pc.oid = pt.typrelid
134+
) sub
119135
group by
120-
t1.oid
136+
oid
121137
) f_args on f_args.oid = f.oid

Diff for: src/lib/types.ts

+1
Original file line numberDiff line numberDiff line change
@@ -148,6 +148,7 @@ const postgresFunctionSchema = Type.Object({
148148
name: Type.String(),
149149
type_id: Type.Number(),
150150
has_default: Type.Boolean(),
151+
table_name: Type.Union([Type.String(), Type.Null()]),
151152
})
152153
),
153154
argument_types: Type.String(),

Diff for: src/server/templates/typescript.ts

+20-1
Original file line numberDiff line numberDiff line change
@@ -343,7 +343,26 @@ export type Database = {
343343
}
344344
345345
return 'unknown'
346-
})()}${fns[0].is_set_returning_function ? '[]' : ''}
346+
})()}${fns[0].is_set_returning_function && fns[0].returns_multiple_rows ? '[]' : ''}
347+
${
348+
// if the function return a set of a table and some definition take in parameter another table
349+
fns[0].returns_set_of_table &&
350+
fns.some((fnd) => fnd.args.length === 1 && fnd.args[0].table_name)
351+
? `SetofOptions: {
352+
from: ${fns
353+
// if the function take a row as first parameter
354+
.filter((fnd) => fnd.args.length === 1 && fnd.args[0].table_name)
355+
.map((fnd) => {
356+
const arg_type = types.find((t) => t.id === fnd.args[0].type_id)
357+
return JSON.stringify(arg_type?.format)
358+
})
359+
.join(' | ')}
360+
to: ${JSON.stringify(fns[0].return_table_name)}
361+
isOneToOne: ${fns[0].returns_multiple_rows ? false : true}
362+
}
363+
`
364+
: ''
365+
}
347366
}`
348367
)
349368
})()}

Diff for: test/lib/functions.ts

+23-20
Original file line numberDiff line numberDiff line change
@@ -4,20 +4,21 @@ import { pgMeta } from './utils'
44
test('list', async () => {
55
const res = await pgMeta.functions.list()
66
expect(res.data?.find(({ name }) => name === 'add')).toMatchInlineSnapshot(
7-
{ id: expect.any(Number) },
8-
`
7+
{ id: expect.any(Number) }, `
98
{
109
"args": [
1110
{
1211
"has_default": false,
1312
"mode": "in",
1413
"name": "",
14+
"table_name": null,
1515
"type_id": 23,
1616
},
1717
{
1818
"has_default": false,
1919
"mode": "in",
2020
"name": "",
21+
"table_name": null,
2122
"type_id": 23,
2223
},
2324
],
@@ -45,8 +46,7 @@ test('list', async () => {
4546
"schema": "public",
4647
"security_definer": false,
4748
}
48-
`
49-
)
49+
`)
5050
})
5151

5252
test('list set-returning function with single object limit', async () => {
@@ -60,6 +60,7 @@ test('list set-returning function with single object limit', async () => {
6060
"has_default": false,
6161
"mode": "in",
6262
"name": "user_row",
63+
"table_name": "users",
6364
"type_id": 16395,
6465
},
6566
],
@@ -105,6 +106,7 @@ test('list set-returning function with multiples definitions', async () => {
105106
"has_default": false,
106107
"mode": "in",
107108
"name": "user_row",
109+
"table_name": "users",
108110
"type_id": 16395,
109111
},
110112
],
@@ -142,6 +144,7 @@ test('list set-returning function with multiples definitions', async () => {
142144
"has_default": false,
143145
"mode": "in",
144146
"name": "todo_row",
147+
"table_name": "todos",
145148
"type_id": 16404,
146149
},
147150
],
@@ -229,21 +232,22 @@ test('retrieve, create, update, delete', async () => {
229232
config_params: { search_path: 'hooks, auth', role: 'postgres' },
230233
})
231234
expect(res).toMatchInlineSnapshot(
232-
{ data: { id: expect.any(Number) } },
233-
`
235+
{ data: { id: expect.any(Number) } }, `
234236
{
235237
"data": {
236238
"args": [
237239
{
238240
"has_default": false,
239241
"mode": "in",
240242
"name": "a",
243+
"table_name": null,
241244
"type_id": 21,
242245
},
243246
{
244247
"has_default": false,
245248
"mode": "in",
246249
"name": "b",
250+
"table_name": null,
247251
"type_id": 21,
248252
},
249253
],
@@ -278,25 +282,25 @@ test('retrieve, create, update, delete', async () => {
278282
},
279283
"error": null,
280284
}
281-
`
282-
)
285+
`)
283286
res = await pgMeta.functions.retrieve({ id: res.data!.id })
284287
expect(res).toMatchInlineSnapshot(
285-
{ data: { id: expect.any(Number) } },
286-
`
288+
{ data: { id: expect.any(Number) } }, `
287289
{
288290
"data": {
289291
"args": [
290292
{
291293
"has_default": false,
292294
"mode": "in",
293295
"name": "a",
296+
"table_name": null,
294297
"type_id": 21,
295298
},
296299
{
297300
"has_default": false,
298301
"mode": "in",
299302
"name": "b",
303+
"table_name": null,
300304
"type_id": 21,
301305
},
302306
],
@@ -331,29 +335,29 @@ test('retrieve, create, update, delete', async () => {
331335
},
332336
"error": null,
333337
}
334-
`
335-
)
338+
`)
336339
res = await pgMeta.functions.update(res.data!.id, {
337340
name: 'test_func_renamed',
338341
schema: 'test_schema',
339342
definition: 'select b - a',
340343
})
341344
expect(res).toMatchInlineSnapshot(
342-
{ data: { id: expect.any(Number) } },
343-
`
345+
{ data: { id: expect.any(Number) } }, `
344346
{
345347
"data": {
346348
"args": [
347349
{
348350
"has_default": false,
349351
"mode": "in",
350352
"name": "a",
353+
"table_name": null,
351354
"type_id": 21,
352355
},
353356
{
354357
"has_default": false,
355358
"mode": "in",
356359
"name": "b",
360+
"table_name": null,
357361
"type_id": 21,
358362
},
359363
],
@@ -388,25 +392,25 @@ test('retrieve, create, update, delete', async () => {
388392
},
389393
"error": null,
390394
}
391-
`
392-
)
395+
`)
393396
res = await pgMeta.functions.remove(res.data!.id)
394397
expect(res).toMatchInlineSnapshot(
395-
{ data: { id: expect.any(Number) } },
396-
`
398+
{ data: { id: expect.any(Number) } }, `
397399
{
398400
"data": {
399401
"args": [
400402
{
401403
"has_default": false,
402404
"mode": "in",
403405
"name": "a",
406+
"table_name": null,
404407
"type_id": 21,
405408
},
406409
{
407410
"has_default": false,
408411
"mode": "in",
409412
"name": "b",
413+
"table_name": null,
410414
"type_id": 21,
411415
},
412416
],
@@ -441,8 +445,7 @@ test('retrieve, create, update, delete', async () => {
441445
},
442446
"error": null,
443447
}
444-
`
445-
)
448+
`)
446449
res = await pgMeta.functions.retrieve({ id: res.data!.id })
447450
expect(res).toMatchObject({
448451
data: null,

0 commit comments

Comments
 (0)