Skip to content

Conversation

avallete
Copy link
Member

@avallete avallete commented Aug 5, 2025

What kind of change does this PR introduce?

  • Add all the necessary informations for embeded functions type inferences to work in postgrest-js (see: feat(types): add embeded functions type inference postgrest-js#632 )

  • Canary tested that it's mostly retro-compatible by generating the types on the infrastructure codebase. The new type definition didn't raised much type errors but we don't use a lot of functions/rpc calls in it. So there is still a chance for this to break some codebases. Best approach might be to release this one as an -rc release so it can be opt-in via local cli echo "vXX-rc" > supabase/.temp/postgres-meta-version && supabase gen types

That way with a canary release of the postgrest-js and the supabase-js we might release gradually with a way for people to opt-out if it causes issue.

I'm open to suggestion about how to release this !

Closes: PGMETA-61

- Introspect the setof function fields for functions
- Restore functions as unions of args + returns
@avallete avallete force-pushed the feat/add-functions-setof-type-introspection-v2 branch from 317219e to 64a1afc Compare August 5, 2025 15:39
@coveralls
Copy link

coveralls commented Aug 6, 2025

Pull Request Test Coverage Report for Build 17789949268

Details

  • 333 of 335 (99.4%) changed or added relevant lines in 4 files are covered.
  • No unchanged relevant lines lost coverage.
  • Overall coverage increased (+0.6%) to 82.866%

Changes Missing Coverage Covered Lines Changed/Added Lines %
src/server/templates/typescript.ts 326 328 99.39%
Totals Coverage Status
Change from base Build 17779696252: 0.6%
Covered Lines: 5879
Relevant Lines: 6955

💛 - Coveralls

or (
select
c.relkind ${includeTableTypes ? `in ('c', 'r')` : `= 'c'`}
c.relkind ${includeTableTypes ? `in ('c', 'r', 'v')` : `= 'c'`}
Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

comment

Include functions that loops to a view for embedded functions.

select 1 from pg_class c
where c.oid = rt.typrelid
-- exclude custom types relation from what is considered a set of table
and c.relkind in ('r', 'p', 'v', 'm', 'f')
Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

comment

All possible function embeding relations:

r | ordinary table
p | partitioned table
v | view
m | materialized view
f | foreign table

Comment on lines +38 to +42
for (const column of columns) {
if (column.table_id in columnsByTableId) {
columnsByTableId[column.table_id].push(column)
}
}
Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

comment

Use traditional for loop to avoid the need to loop twice for filter then foreach/push.

Comment on lines +129 to +135
for (const schema in introspectionBySchema) {
introspectionBySchema[schema].tables.sort((a, b) => a.name.localeCompare(b.name))
introspectionBySchema[schema].views.sort((a, b) => a.name.localeCompare(b.name))
introspectionBySchema[schema].functions.sort((a, b) => a.fn.name.localeCompare(b.fn.name))
introspectionBySchema[schema].enums.sort((a, b) => a.name.localeCompare(b.name))
introspectionBySchema[schema].compositeTypes.sort((a, b) => a.name.localeCompare(b.name))
}
Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

comment

Group the place where we sort things for consistency to dedup the places where sorts are needed.

// OR if the function takes a table row but doesn't qualify as embedded (for error reporting)
(inArgs[0].table_name && !func.return_table_name)))
) {
introspectionBySchema[func.schema].functions.push({ fn: func, inArgs })
Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

comment

We save the inArgs along with the function definition as it'll be needed in many places when generating the function return types. Since it's already available keep it along for re-use.

if (conflictingFns.length > 0) {
const conflictingFn = conflictingFns[0]
const returnTypeName = typesById[conflictingFn.fn.return_type_id]?.name || 'unknown'
return `Could not choose the best candidate function between: ${schema.name}.${fn.name}(), ${schema.name}.${fn.name}( => ${returnTypeName}). Try renaming the parameters or the function itself in the database so function overloading can be resolved`
Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

comment

This'll be used to warn the user at compile time that his function won't be available in postgrest due to conflicts.

}
${
'is_updatable' in view && view.is_updatable
view.is_updatable
Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Comment on lines +619 to +623
for (const fnName in schemaFunctionsGroupedByName) {
schemaFunctionsGroupedByName[fnName].sort((a, b) =>
b.fn.definition.localeCompare(a.fn.definition)
)
}
Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

comment

With functions having multiple definition overrides (same name) we sort them by definition text value. We might want to do another approach to have more stability 🤔

@avallete avallete marked this pull request as ready for review August 6, 2025 17:27
@avallete avallete requested review from a team as code owners August 6, 2025 17:27
@avallete avallete requested a review from soedirgo August 7, 2025 08:18
Copy link

github-actions bot commented Sep 16, 2025

🚀 Canary Deployment Status

Canary image deployed successfully!

🐳 Docker Image: supabase/postgres-meta:canary-pr-971-1e4daa0e55e4672c04d026423c1a54329ff13376
📝 Commit: 1e4daa0e55e4672c04d026423c1a54329ff13376

You can test this canary deployment by pulling the image:

docker pull supabase/postgres-meta:canary-pr-971-1e4daa0e55e4672c04d026423c1a54329ff13376

You can also set the version in a supabase local project by running:

echo "supabase/postgres-meta:canary-pr-971-1e4daa0e55e4672c04d026423c1a54329ff13376" > supabase/.temp/pgmeta-version

Or use it in your docker-compose.yml:

services:
  postgres-meta:
    image: supabase/postgres-meta:canary-pr-971-1e4daa0e55e4672c04d026423c1a54329ff13376
    # ... other configuration

The canary image is available on:

false


Last updated: 2025-09-17T07:35:04Z

@avallete avallete added the deploy-canary Deploy a canary image for postgres-meta label Sep 16, 2025
Comment on lines +88 to +106
case
when f.proretset and rt.typrelid != 0 and exists (
select 1 from pg_class c
where c.oid = rt.typrelid
-- exclude custom types relation from what is considered a set of table
and c.relkind in ('r', 'p', 'v', 'm', 'f')
) then true
else false
end as returns_set_of_table,
case
when rt.typrelid != 0 then
(select relname from pg_class where oid = rt.typrelid)
else null
end as return_table_name,
case
when f.proretset then
coalesce(f.prorows, 0) > 1
else false
end as returns_multiple_rows,
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Let's not do select from pg_class within result column expressions - this might cause performance issues.

returns_set_of_table should be redundant with is_set_returning_function and return_type_relation_id (is_set_returning_function && return_type_relation_id !== null)

And return_table_name should be redundant with return_type_relation_id since we can get the table name from the /tables output using the relation id

when f.proretset then
coalesce(f.prorows, 0) > 1
else false
end as returns_multiple_rows,
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I'm thinking instead of returns_multiple_rows we can just return prorows as is (setting it as null if 0)

It's easy to confuse it with is_set_returning_function since they mean the same thing; rather what we're doing is adhering to PostgREST's idiosyncrasies

@soedirgo
Copy link
Member

Can we split out the refactor into another PR? It's difficult to see which change is related to computed relationships.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
deploy-canary Deploy a canary image for postgres-meta
Projects
None yet
Development

Successfully merging this pull request may close these issues.

3 participants