Skip to content

Conversation

@francesco-carrella
Copy link
Contributor

@francesco-carrella francesco-carrella commented Dec 3, 2025

Fixes #191. Fixes #77.

Problem

  1. Migrations failing if enum types exist in different schemas #191: Migrations fail when users have enums with the same name in public schema (e.g., public.pricing_type). The enum existence checks weren't schema-scoped.

  2. 0012_add_updated_at.sql hardcodes role name to postgres #77: Migration 0012_add_updated_at.sql hardcodes OWNER TO postgres, failing for users without a postgres role.

We couldn't fix either because pg-node-migrations validates checksums and rejects modified migration files.

Solution

Replace pg-node-migrations with a custom runner that skips checksum validation, then fix the migrations.

Changes

  • migrate.ts: Custom runner with advisory locks, transactions, automatic hash column removal for upgrades
  • 0003, 0004, 0005, 0024: Schema-scoped enum checks (pg_type JOIN pg_namespace)
  • 0012: Removed hardcoded OWNER TO postgres
  • package.json: Removed pg-node-migrations dependency

Testing

  • Fresh install
  • Upgrade from pg-node-migrations (hash column dropped, no re-runs)
  • Concurrent migrations (advisory lock)
  • Conflicting enums in public schema

Compatibility

Compared with pg-node-migrations source:

  • Transaction handling identical
  • Advisory lock same outcome (blocking vs retry loop)
  • -- postgres-migrations disable-transaction supported
  • Hash column dropped on upgrade
  • No JS migration support (none exist)

Open questions

  1. No migration uses -- postgres-migrations disable-transaction. Remove?
  2. ROLLBACK failures silently caught. Log instead?

@kevcodez
Copy link
Contributor

kevcodez commented Dec 3, 2025

Thanks for the contribution already!

For the new migrations, what would be very useful is to also support some type of placeholders - i.e. the stripe schema is technically configurable to be any other schema, but all migrations are hardcoded to stripe. We should instead pass in the configured Stripe schema to the migrations and make use of that. The migrations would need to contain some type of placeholder logic and the placeholders would need to be replaced before applying the migrations. This does not have to be overly complex

@francesco-carrella
Copy link
Contributor Author

Good point on the schema placeholder. Two approaches come to mind:

Option 1: Multi-pattern regex

sql
  .replace(/"stripe"\./g, `"${schema}".`)
  .replace(/\bstripe\./g, `${schema}.`)
  .replace(/'stripe'/g, `'${schema}'`)
  .replace(/\bstripe_/g, `${schema}_`)  // index names

Fragile - easy to miss edge cases.

Option 2: Explicit placeholders
Update all migrations to use {{schema}} (or similar), then:

sql.replace(/\{\{schema\}\}/g, config.schema)

More upfront work but explicit and safe.

I'm leaning toward Option 2 - cleaner and less error-prone. Which direction would you prefer?

@francesco-carrella
Copy link
Contributor Author

One more thought: Option 2 would make the migration files invalid SQL on their own - can't just run them with psql. Option 1 keeps them valid and defaults to stripe, though it requires some care when writing future migrations to follow the same patterns.

@kevcodez
Copy link
Contributor

kevcodez commented Dec 3, 2025

I'd still opt for option 2 as option 1 can lead to accidental replacements

When someone tries running the SQL files directly, it would be easy enough to do a manual replacement and it also leads to making a more explicit choice of the schema

Could also have a utility function to print out all schema files with placeholders replaced, but that is more of a nice to have

- Custom migration runner with advisory locks (no checksum validation)
- Use {{schema}} placeholders for configurable schema support
- Add getMigrations(schema) export for inspecting migrations

Fixes supabase#191, fixes supabase#77
@francesco-carrella francesco-carrella force-pushed the fix/custom-migration-runner branch from 731217c to 48220b7 Compare December 3, 2025 16:23
@francesco-carrella
Copy link
Contributor Author

francesco-carrella commented Dec 3, 2025

Done, went with Option 2 - all migrations now use {{schema}} placeholders.
Also added getMigrations(schema) for inspecting migrations.

Also normalized quoting across all the migrations files while at it.

@francesco-carrella
Copy link
Contributor Author

francesco-carrella commented Dec 4, 2025

I've been thinking, probably getMigrations({ schema }) would be more consistent with runMigrations({ ... })?

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

Migrations failing if enum types exist in different schemas 0012_add_updated_at.sql hardcodes role name to postgres

2 participants