Skip to content

Slow seeding when having a large seed file and an anonymous block. #6686

Closed

Description

@Rinze-Smits

Affected area

Seeding

Supabase CLI version

2.117.0

Operating system

linux x64

Installation method

No response

Command

supabase db reset

Actual output

Very slow seed execution, over one minute

Expected behavior

Fast seed, as fast as when directly running with postgres, milliseconds at most.

Steps to reproduce

Have a seed file that looks like

DO $$
BEGIN

/* 1 MB block of SQL code */

END $$;

Run the seed, e.g. by supabase db reset.
This takes over a minute to run, even if the sql itself is fast.

Removing the anonymous block makes it fast. It looks like the seed sql parser has some quadratic complexity for finding the closing $$.
Other tags with open/close semantics also cause slowness, like /* ... */ comments, a large file with those is also slow. Single line comments -- are not slow.

Crash report ID

No response

Docker and service versions

Additional context

No response

Activity

  1. iamsharduld commented on Sep 21, 2026

    @iamsharduld

    Reproduced on 2.117.0 and current develop. The statement splitter is quadratic in statement length. Root cause and a small fix below.

    Cause: splitRaw (apps/cli/src/command-internal/sql-split.ts) builds the current statement with acc += rune, which Bun stores as a rope. The states then slice two or more characters from near the end of acc. Each such slice copies the whole rope, so it costs O(statement length). That happens:

    • on every rune inside a $$/$tag$ body (DollarState) or a /* */ comment (BlockState)
    • on every c/C (isBeginAtomic) and $ (hasIdentifierRuneBefore) outside quotes and comments

    -- comments are unaffected because their delimiter is a single \n, and a one-character slice doesn't copy the rope. Migrations use the same splitter (parseMigrationContent in migration-file.ts), so a large DO $$ migration is just as slow.

    Fix: track offsets and slice the flat input instead of concatenating. Only splitRaw changes:

    let start = 0;
    let end = 0;
    for (const rune of sql) {
      end += rune.length;
      const acc = sql.slice(start, end);
      const next = state.next(rune, acc);
      if (next === null) {
        tokens.push({ text: acc, terminated: true });
        start = end;
        state = new ReadyState();
      } else {
        state = next;
      }
    }
    if (end > start) tokens.push({ text: sql.slice(start), terminated: false });

    Results (Bun 1.4.1, Apple M1 Pro):

    splitAndTrim, one statement 256 KB 512 KB 1 MB
    DO $$ … $$, before → after 1,886 → 10 ms 6,177 → 20 ms 21,897 → 27 ms
    /* … */, before → after 2,022 → 10 ms 6,299 → 18 ms 22,098 → 32 ms

    End to end, supabase db reset with a 0.95 MiB DO $$ seed takes about 22 s on both 2.117.0 and develop, and 0.67 s with the fix. The same block as a migration takes 0.69 s with the fix. Database state and CLI output are identical.

    sql-split.unit.test.ts passes unchanged. splitSqlTokens output also matches the current implementation on edge cases (nested block comments, tagged dollar quotes, BEGIN ATOMIC, doubled quotes, non-BMP characters, unterminated trailing statements) and on all the slow inputs.

    Happy to open a PR with this and a regression test. Could this get the open-for-contribution label?

  2. self-assigned this
    on Sep 21, 2026
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Metadata

Metadata

Assignees

Type

No type

Projects

No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions