import { source } from 'common-tags' import { executeQuery } from './query' import { PgMetaDatabaseError, WrappedResult } from './types' import { assertSelfHosted } from './util' import { makeRandomString } from '@/lib/helpers' export type ListMigrationsResult = { version: string name?: string } const listMigrationVersionsQuery = () => 'select version, name from supabase_migrations.schema_migrations order by version' const initializeHistoryTableQuery = () => `begin; create schema if not exists supabase_migrations; create table if not exists supabase_migrations.schema_migrations (version text not null primary key); alter table supabase_migrations.schema_migrations add column if not exists statements text[]; alter table supabase_migrations.schema_migrations add column if not exists name text; commit;` const applyAndTrackMigrationsQuery = (query: string, name?: string) => { // Escapes literals using postgres dollar quoted string const dollar = `$${makeRandomString(20)}$` const quote = (s?: string) => (s ? dollar + s + dollar : `''`) return source` begin; -- apply sql from post body ${query}; -- track statements in history table insert into supabase_migrations.schema_migrations (version, name, statements) values ( to_char(current_timestamp, 'YYYYMMDDHH24MISS'), ${quote(name)}, array[${quote(query)}] ); commit; ` } export type ListMigrationVersionsOptions = { headers?: HeadersInit } /** * Lists all migrations in the migrations history table. * * _Only call this from server-side self-hosted code._ */ export async function listMigrationVersions({ headers, }: ListMigrationVersionsOptions): Promise> { assertSelfHosted() const { data, error } = await executeQuery({ query: listMigrationVersionsQuery(), headers, }) if (error) { // Return empty list if the migrations table doesn't exist if (error instanceof PgMetaDatabaseError && error.code === '42P01') { return { data: [], error: undefined } } return { data: undefined, error } } return { data, error: undefined } } export type ApplyAndTrackMigrationsOptions = { query: string name?: string headers?: HeadersInit } /** * Applies a SQL migration and tracks it in the migrations history table. * * _Only call this from server-side self-hosted code._ */ export async function applyAndTrackMigrations({ query, name, headers, }: ApplyAndTrackMigrationsOptions): Promise> { assertSelfHosted() const initializeResponse = await executeQuery({ query: initializeHistoryTableQuery(), headers, }) if (initializeResponse.error) { return initializeResponse } const applyAndTrackResponse = await executeQuery({ query: applyAndTrackMigrationsQuery(query, name), headers, }) return applyAndTrackResponse }