supabase / postgres-meta

A RESTful API for managing your Postgres. Fetch tables, add roles, and run queries
https://supabase.com
Apache License 2.0
941 stars 126 forks source link

Generated typescript types ordering is nondeterministic for: View Relationships #810

Closed StephenTangCook closed 1 month ago

StephenTangCook commented 10 months ago

Bug report

Describe the bug

Apologies beforehand if this is the wrong repo -- I wasn't sure if the root of the issue was in type generation (another repo?) or just in the translation to typescript.

We ran into a bug where the generated typescript types were nondeterminstic using supabase gen types typescript --local. There is an unordered list or a race condition that causes the order of types to change randomly. This is blocking our Github action to diff the generated types file.

The scenario that at least we repro'd is when it generates the types for a View that contains multiple FKs to the same table. In our case the table is self-referential but I don't know if that is a requirement. Here's a contrived example:

Let's say we have the following schema for a post (table), which can point to a parent post: id (uuid) body (text) parent_post_id (FK to post id, nullable)
create table
  public.post (
    id uuid not null default gen_random_uuid (),
    body text not null,
    parent_post_id uuid null,
    constraint post_pkey primary key (id),
    constraint post_parent_post_id_fkey foreign key (parent_post_id) references post (id)
  ) tablespace pg_default;

Then let's say we create a view where I get all the posts and some info about the parent post, if it has one:

create view
  public.my_view as
select
  p.id as post_id,
  p.body as post_body,
  p.parent_post_id as post_parent_post_id,
  parent_p.id as parent_post_id,
  parent_p.body as parent_post_body
from
  post p
  left join post parent_p on p.parent_post_id = parent_p.id;

When we generate the types, the view types will be:

Views: {
      my_view: {
        Row: {
          parent_post_body: string | null
          parent_post_id: string | null
          post_body: string | null
          post_id: string | null
          post_parent_post_id: string | null
        }
        Relationships: [
          {
            foreignKeyName: "post_parent_post_id_fkey"
            columns: ["post_parent_post_id"]
            isOneToOne: false
            referencedRelation: "post"
            referencedColumns: ["id"]
          },
          {
            foreignKeyName: "post_parent_post_id_fkey"
            columns: ["post_parent_post_id"]
            isOneToOne: false
            referencedRelation: "my_view"
            referencedColumns: ["post_id"]
          },
          {
            foreignKeyName: "post_parent_post_id_fkey"
            columns: ["post_parent_post_id"]
            isOneToOne: false
            referencedRelation: "my_view"
            referencedColumns: ["parent_post_id"]
          }
        ]
      }
    }

The issue is that the order of the Relationships objects can change for reasons we haven't identified. It's difficult to repro locally with on the same commit, but after some code changes that do not affect the schema(?), or consistently on Github actions, we'll hit this. The result is a diff like:

-            referencedColumns: ["id"]
+            referencedColumns: ["parent_post_id"]
...
-            referencedColumns: ["parent_post_id"]
+            referencedColumns: ["id"]

Here's our Github action:

jobs:
  build:
    name: Validate Types
    timeout-minutes: 10
    runs-on: ubuntu-latest
    steps:
      - uses: actions/checkout@v4

      - uses: supabase/setup-cli@v1.2.0
        with:
          version: 1.127.3

      - name: Start Supabase local development setup
        run: supabase db start

      - name: Verify generated types are up-to-date
        run: |
          supabase gen types typescript --local > packages/db-types/database.types.ts
          if [ "$(git diff --ignore-space-at-eol packages/db-types/database.types.ts | wc -l)" -gt "0" ]; then
            echo "Detected uncommitted changes after build. See status below:"
            git diff
            exit 1
          fi

To Reproduce

Steps to reproduce the behavior, please provide code snippets or a repository:

  1. Create a table with a self-referential FK to the id of the same table
  2. Create a view that joins the table columns along with the columns of the related row, if non-null
  3. Generate the typescript types
  4. [pre-requisite env causes unknown] The ordering of the Relationships of the View types can change.

Expected behavior

The ordering of the Relationships of the View types is consistent.

System information

Additional context