8000 Merge pull request #500 from supabase/fix/typegen-set-returning-function · joeally/postgres-meta@eb5190a · GitHub
[go: up one dir, main page]

Skip to content

Commit eb5190a

Browse files
authored
Merge pull request supabase#500 from supabase/fix/typegen-set-returning-function
fix(typegen): set returning function
2 parents a1e55cf + c50a5e1 commit eb5190a

File tree

6 files changed

+69
-30
lines changed

6 files changed

+69
-30
lines changed

src/lib/constants.ts

Lines changed: 1 addition & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -1,7 +1 @@
1-
export const DEFAULT_SYSTEM_SCHEMAS = [
2-
'information_schema',
3-
'pg_catalog',
4-
'pg_temp_1',
5-
'pg_toast',
6-
'pg_toast_temp_1',
7-
]
1+
export const DEFAULT_SYSTEM_SCHEMAS = ['information_schema', 'pg_catalog', 'pg_toast']

src/lib/sql/functions.sql

Lines changed: 5 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -25,7 +25,7 @@ with functions as (
2525
p.prokind = 'f'
2626
)
2727
select
28-
f.oid :: int8 as id,
28+
f.oid::int8 as id,
2929
n.nspname as schema,
3030
f.proname as name,
3131
l.lanname as language,
@@ -40,7 +40,9 @@ select
4040
coalesce(f_args.args, '[]') as args,
4141
pg_get_function_arguments(f.oid) as argument_types,
4242
pg_get_function_identity_arguments(f.oid) as identity_argument_types,
43-
t.typname as return_type,
43+
rt.typname as return_type,
44+
nullif(rt.typrelid::int8, 0) as return_type_relation_id,
45+
f.proretset as is_set_returning_function,
4446
case
4547
when f.provolatile = 'i' then 'IMMUTABLE'
4648
when f.provolatile = 's' then 'STABLE'
@@ -52,7 +54,7 @@ from
5254
functions f
5355
left join pg_namespace n on f.pronamespace = n.oid
5456
left join pg_language l on f.prolang = l.oid
55-
left join pg_type t on t.oid = f.prorettype
57+
left join pg_type rt on rt.oid = f.prorettype
5658
left join (
5759
select
5860
oid,

src/lib/sql/schemas.sql

Lines changed: 10 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -1,15 +1,17 @@
11
-- Adapted from information_schema.schemata
22

3-
SELECT
4-
n.oid :: int8 AS id,
5-
n.nspname AS name,
6-
u.rolname AS owner
7-
FROM
3+
select
4+
n.oid::int8 as id,
5+
n.nspname as name,
6+
u.rolname as owner
7+
from
88
pg_namespace n,
99
pg_roles u
10-
WHERE
10+
where
1111
n.nspowner = u.oid
12-
AND (
12+
and (
1313
pg_has_role(n.nspowner, 'USAGE')
14-
OR has_schema_privilege(n.oid, 'CREATE, USAGE')
14+
or has_schema_privilege(n.oid, 'CREATE, USAGE')
1515
)
16+
and not pg_catalog.starts_with(n.nspname, 'pg_temp_')
17+
and not pg_catalog.starts_with(n.nspname, 'pg_toast_temp_')

src/lib/types.ts

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -108,6 +108,8 @@ const postgresFunctionSchema = Type.Object({
108108
argument_types: Type.String(),
109109
identity_argument_types: Type.String(),
110110
return_type: Type.String(),
111+
return_type_relation_id: Type.Union([Type.Integer(), Type.Null()]),
112+
is_set_returning_function: Type.Boolean(),
111113
behavior: Type.Union([
112114
Type.Literal('IMMUTABLE'),
113115
Type.Literal('STABLE'),

src/server/templates/typescript.ts

Lines changed: 41 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -222,7 +222,12 @@ export interface Database {
222222
([fnName, fns]) =>
223223
`${JSON.stringify(fnName)}: ${fns
224224
.map(
225-
({ args, return_type }) => `{
225+
({
226+
args,
227+
return_type,
228+
return_type_relation_id,
229+
is_set_returning_function,
230+
}) => `{
226231
Args: ${(() => {
227232
const inArgs = args.filter(({ mode }) => mode === 'in')
228233
@@ -252,14 +257,16 @@ export interface Database {
252257
return { name, type: 'unknown', has_default }
253258
})
254259
255-
return `{ ${argsNameAndType.map(
256-
({ name, type, has_default }) =>
257-
`${JSON.stringify(name)}${has_default ? '?' : ''}: ${type}`
258-
)} }`
260+
return `{
261+
${argsNameAndType.map(
262+
({ name, type, has_default }) =>
263+
`${JSON.stringify(name)}${has_default ? '?' : ''}: ${type}`
264+
)}
265+
}`
259266
})()}
260-
Returns: ${(() => {
267+
Returns: (${(() => {
268+
// Case 1: `returns table`.
261269
const tableArgs = args.filter(({ mode }) => mode === 'table')
262-
263270
if (tableArgs.length > 0) {
264271
const argsNameAndType = tableArgs.map(({ name, type_id }) => {
265272
let type = arrayTypes.find(({ id }) => id === type_id)
@@ -278,13 +285,35 @@ export interface Database {
278285
return { name, type: 'unknown' }
279286
})
280287
281-
return `{ ${argsNameAndType.map(
282-
({ name, type }) => `${JSON.stringify(name)}: ${type}`
283-
)} }[]`
288+
return `{
289+
${argsNameAndType.map(
290+
({ name, type }) => `${JSON.stringify(name)}: ${type}`
291+
)}
292+
}`
293+
}
294+
295+
// Case 2: returns a relation's row type.
296+
const relation = [...tables, ...views].find(
297+
({ id }) => id === return_type_relation_id
298+
)
299+
if (relation) {
300+
return `{
301+
${relation.columns
302+
.sort(({ name: a }, { name: b }) => a.localeCompare(b))
303+
.map(
304+
(column) =>
305+
`${JSON.stringify(column.name)}: ${pgTypeToTsType(
306+
column.format,
307+
types,
308+
schemas
309+
)} ${column.is_nullable ? '| null' : ''}`
310+
)}
311+
}`
284312
}
285313
314+
// Case 3: returns base/composite/enum type.
286315
return pgTypeToTsType(return_type, types, schemas)
287-
})()}
316+
})()})${is_set_returning_function ? '[]' : ''}
288317
}`
289318
)
290319
.join('|')}`
@@ -367,7 +396,7 @@ const pgTypeToTsType = (
367396
} else if (pgType === 'void') {
368397
return 'undefined'
369398
} else if (pgType === 'record') {
370-
return 'Record<string, unknown>[]'
399+
return 'Record<string, unknown>'
371400
} else if (pgType.startsWith('_')) {
372401
return `(${pgTypeToTsType(pgType.substring(1), types, schemas)})[]`
373402
} else {

test/lib/functions.ts

Lines changed: 10 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -32,9 +32,11 @@ test('list', async () => {
3232
"definition": "select $1 + $2;",
3333
"id": Any<Number>,
3434
"identity_argument_types": "integer, integer",
35+
"is_set_returning_function": false,
3536
"language": "sql",
3637
"name": "add",
3738
"return_type": "int4",
39+
"return_type_relation_id": null,
3840
"schema": "public",
3941
"security_definer": false,
4042
}
@@ -129,9 +131,11 @@ test('retrieve, create, update, delete', async () => {
129131
"definition": "select a + b",
130132
"id": Any<Number>,
131133
"identity_argument_types": "a smallint, b smallint",
134+
"is_set_returning_function": false,
132135
"language": "sql",
133136
"name": "test_func",
134137
"return_type": "int4",
138+
"return_type_relation_id": null,
135139
"schema": "public",
136140
"security_definer": true,
137141
},
@@ -176,9 +180,11 @@ test('retrieve, create, update, delete', async () => {
176180
"definition": "select a + b",
177181
"id": Any<Number>,
178182
"identity_argument_types": "a smallint, b smallint",
183+
"is_set_returning_function": false,
179184
"language": "sql",
180185
"name": "test_func",
181186
"return_type": "int4",
187+
"return_type_relation_id": null,
182188
"schema": "public",
183189
"security_definer": true,
184190
},
@@ -227,9 +233,11 @@ test('retrieve, create, update, delete', async () => {
227233
"definition": "select b - a",
228234
"id": Any<Number>,
229235
"identity_argument_types": "a smallint, b smallint",
236+
"is_set_returning_function": false,
230237
"language": "sql",
231238
"name": "test_func_renamed",
232239
"return_type": "int4",
240+
"return_type_relation_id": null,
233241
"schema": "test_schema",
234242
"security_definer": true,
235243
},
@@ -274,9 +282,11 @@ test('retrieve, create, update, delete', async () => {
274282
"definition": "select b - a",
275283
"id": Any<Number>,
276284
"identity_argument_types": "a smallint, b smallint",
285+
"is_set_returning_function": false,
277286
"language": "sql",
278287
"name": "test_func_renamed",
279288
"return_type": "int4",
289+
"return_type_relation_id": null,
280290
"schema": "test_schema",
281291
"security_definer": true,
282292
},

0 commit comments

Comments
 (0)
0