Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
13 changes: 7 additions & 6 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -94,12 +94,13 @@ pgroll [global options] <command>

### Global options

| Option | Description |
| --------------------------- | --------------------------------------------------------------- |
| `-d, --migrationDir <path>` | Directory holding the migration files (default `./migrations`). |
| `-u, --url <url>` | PostgreSQL connection URL (overrides `PG*` env vars). |
| `-V, --version` | Print the `pgroll` version. |
| `-h, --help` | Show help. |
| Option | Description |
| --------------------------- | --------------------------------------------------------------- |
| `-d, --migrationDir <path>` | Directory holding the migration files (default: `./migrations`). |
| `-u, --url <url>` | PostgreSQL connection URL (overrides `PG*` env vars). |
| `-s, --schema <schema>` | Schema for pgroll's internal migrations table (default: public). |
| `-V, --version` | Print the `pgroll` version. |
| `-h, --help` | Show help. |

### Commands

Expand Down
7 changes: 4 additions & 3 deletions src/cli.ts
Original file line number Diff line number Diff line change
Expand Up @@ -16,16 +16,17 @@ let migrator: IMigrator;
program
.version('0.0.9')
.description('Database migration tool')
.option('-d, --migrationDir <filepath>', 'Specify migration directory(Default: ./migrations)')
.option('-d, --migrationDir <filepath>', 'Specify migration directory (Default: ./migrations)')
.option('-u, --url <url>', 'PostgreSQL connection URL (overrides PG* env vars)')
.option('-s, --schema <schema>', 'Specify schema (Default: public)')
.hook('preAction', cmd => {
const opts = cmd.opts<{ migrationDir?: string; url?: string }>();
const opts = cmd.opts<{ migrationDir?: string; url?: string; schema?: string }>();
const pgOptions = {
onnotice: () => {
// do nothing
}
};
migrator = new Migrator(opts.url ? postgres(opts.url, pgOptions) : postgres(pgOptions), opts.migrationDir);
migrator = new Migrator(opts.url ? postgres(opts.url, pgOptions) : postgres(pgOptions), opts.migrationDir, opts.schema);
});

program
Expand Down
35 changes: 15 additions & 20 deletions src/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -10,6 +10,7 @@ interface Option {

export interface IMigrator {
migrationsDir: string;
schema: string;
up: (opts?: Option) => Promise<void>;
down: (opts?: Option) => Promise<void>;
go: (version: number, opts?: Option) => Promise<void>;
Expand All @@ -19,14 +20,16 @@ export interface IMigrator {
export class Migrator implements IMigrator {
private readonly dbClient: Sql;
readonly migrationsDir: string;
readonly schema: string;

constructor(dbClient: Sql, migrationsDir = '') {
constructor(dbClient: Sql, migrationsDir = '', schema = '') {
this.dbClient = dbClient;
this.migrationsDir = migrationsDir || `${process.cwd()}/migrations`;
this.schema = schema ?? "public";
}

async ensureMigrationTable(tx: ReservedSql): Promise<void> {
await tx`CREATE TABLE IF NOT EXISTS migrations(
await tx`CREATE TABLE IF NOT EXISTS ${this.schema}.migrations(
name varchar(500) PRIMARY KEY,
version smallint NOT NULL,
applied_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP);`;
Expand Down Expand Up @@ -57,10 +60,8 @@ export class Migrator implements IMigrator {
const fileVersion = Math.min(fileNames.length, version);
for (let i = currentVersion; i < fileVersion; i++) {
const file = fileNames[i] ?? '';
await Promise.all([
tx.file(path.join(this.migrationsDir, file)).execute(),
tx`INSERT INTO migrations(name, version) VALUES (${file}, ${i} + 1)`
]);
await tx.file(path.join(this.migrationsDir, file)).execute();
await tx`INSERT INTO ${this.schema}.migrations(name, version) VALUES (${file}, ${i} + 1)`;
opts?.eventHandler(`Successfully migrated: ${file}`);
}
if (version > fileNames.length) {
Expand All @@ -73,10 +74,8 @@ export class Migrator implements IMigrator {
const end = start + (currentVersion - version);
for (let i = start; i < end; i++) {
const file = fileNames[i] ?? '';
await Promise.all([
tx.file(path.join(this.migrationsDir, file)).execute(),
tx`DELETE FROM migrations WHERE version = ${fileNames.length - i}`
]);
await tx.file(path.join(this.migrationsDir, file)).execute();
await tx`DELETE FROM ${this.schema}.migrations WHERE version = ${fileNames.length - i}`;
opts?.eventHandler(`Successfully migrated: ${file}`);
}
}
Expand All @@ -102,10 +101,8 @@ export class Migrator implements IMigrator {
for (const fileName of fileNames) {
const id = fileNames.indexOf(fileName);
if (id >= currentVersion) {
await Promise.all([
tx.file(path.join(this.migrationsDir, fileName)).execute(),
tx`INSERT INTO migrations(name, version) VALUES (${fileName}, ${id} + 1)`
]);
await tx.file(path.join(this.migrationsDir, fileName)).execute();
await tx`INSERT INTO ${this.schema}.migrations(name, version) VALUES (${fileName}, ${id} + 1)`;
opts?.eventHandler(`Successfully migrated: ${fileName}`);
}
}
Expand All @@ -114,10 +111,8 @@ export class Migrator implements IMigrator {
const start = fileNames.length - currentVersion;
for (let i = start; i < fileNames.length; i++) {
const file = fileNames[i] ?? '';
await Promise.all([
tx.file(path.join(this.migrationsDir, file)).execute(),
tx`DELETE FROM migrations WHERE version = ${fileNames.length - i}`
]);
await tx.file(path.join(this.migrationsDir, file)).execute();
await tx`DELETE FROM ${this.schema}.migrations WHERE version = ${fileNames.length - i}`;
opts?.eventHandler(`Successfully migrated: ${file}`);
}
}
Expand All @@ -132,12 +127,12 @@ export class Migrator implements IMigrator {
}

async getCurrentVersion(): Promise<number> {
const result = await this.dbClient`SELECT version FROM migrations ORDER BY version DESC LIMIT 1`;
const result = await this.dbClient`SELECT version FROM ${this.schema}.migrations ORDER BY version DESC LIMIT 1`;
return result.length > 0 ? (result[0]?.['version'] as number) : 0;
}

async getCurrentVersionWithTx(tx: ReservedSql): Promise<number> {
const result = await tx`SELECT version FROM migrations ORDER BY version DESC LIMIT 1`;
const result = await tx`SELECT version FROM ${this.schema}.migrations ORDER BY version DESC LIMIT 1`;
return result.length > 0 ? (result[0]?.['version'] as number) : 0;
}

Expand Down