feat(postgres): implement the first version of the PostgreSQL plugin

This commit is contained in:
Joakim Carlstein 2023-12-19 13:18:56 +01:00 committed by Joakim Carlstein
parent 3d34b8ba13
commit 17c4723bb8
16 changed files with 761 additions and 18 deletions

View file

@ -0,0 +1,5 @@
---
'@emigrate/postgres': minor
---
Implement the first version of the @emigrate/postgres plugin

View file

@ -117,6 +117,10 @@ export default defineConfig({
label: 'File System',
link: '/plugins/storage/file-system/',
},
{
label: 'PostgreSQL',
link: '/plugins/storage/postgres/',
},
{
label: 'MySQL',
link: '/plugins/storage/mysql/',
@ -135,6 +139,10 @@ export default defineConfig({
label: 'Default Loader',
link: '/plugins/loaders/default/',
},
{
label: 'PostgreSQL Loader',
link: '/plugins/loaders/postgres/',
},
{
label: 'MySQL Loader',
link: '/plugins/loaders/mysql/',
@ -171,6 +179,10 @@ export default defineConfig({
label: 'JavaScript Generator',
link: '/plugins/generators/js/',
},
{
label: 'PostgreSQL Generator',
link: '/plugins/generators/postgres/',
},
{
label: 'MySQL Generator',
link: '/plugins/generators/mysql/',

View file

@ -41,22 +41,22 @@ But for now, this is the way to go.
Emigrate uses a <Link href="/plugins/storage/">storage plugin</Link> to store the migration history.
Install the plugin you want to use, for example:
Install the plugin you want to use, for example the <Link href="/plugins/storage/postgres/">PostgreSQL Storage</Link>:
<Tabs>
<TabItem label="npm">
```bash
npm install @emigrate/mysql
npm install @emigrate/postgres
```
</TabItem>
<TabItem label="pnpm">
```bash
pnpm add @emigrate/mysql
pnpm add @emigrate/postgres
```
</TabItem>
<TabItem label="yarn">
```bash
yarn add @emigrate/mysql
yarn add @emigrate/postgres
```
</TabItem>
</Tabs>
@ -66,7 +66,7 @@ Install the plugin you want to use, for example:
Create a new migration file in your project using:
```bash title="Create a new migration file"
npx emigrate new --plugin mysql create users table
npx emigrate new --plugin postgres create users table
```
```txt title="Output"
@ -79,12 +79,12 @@ Emigrate new v0.10.0 /your/project/path
```
:::note
The `mysql` plugin is used here to generate a migration file with the `.sql` extension.
The `postgres` plugin is used here to generate a migration file with the `.sql` extension.
Otherwise the file would have the `.js` extension by default.
:::
:::tip[Did you know?]
You can avoid typing `--plugin mysql` by configuring Emigrate using an `emigrate.config.js` file.
You can avoid typing `--plugin postgres` by configuring Emigrate using an `emigrate.config.js` file.
See <Link href="/reference/configuration/">Configuration</Link> for more information.
:::
@ -95,10 +95,9 @@ Open the migration file in your editor and fill it with your SQL query:
```sql title="migrations/20231215125421364_create_users_table.sql" {2-7}
-- Migration: create users table
CREATE TABLE users (
id INT NOT NULL AUTO_INCREMENT,
id SERIAL PRIMARY KEY,
name VARCHAR(255) NOT NULL,
email VARCHAR(255) NOT NULL,
PRIMARY KEY (id)
email VARCHAR(255) NOT NULL
);
```
@ -111,7 +110,7 @@ There's no magic about the first line comment as when using Liquibase, it's just
To show both pending and already applied migrations (or previously failed), use the `list` command:
```bash title="Show all migrations"
npx emigrate list --storage mysql
npx emigrate list --storage postgres
```
```txt title="Example output"
@ -129,7 +128,7 @@ Emigrate list v0.10.0 /your/project/path
A good way to test your configuration is to run the migrations in dry mode:
```bash title="Show pending migrations"
npx emigrate up --storage mysql --plugin mysql --dry
npx emigrate up --storage postgres --plugin postgres --dry
```
:::note
@ -142,6 +141,6 @@ Be sure to configure the connection correctly and use the `--dry` flag to test y
:::
:::tip[Did you know?]
In the example above the `@emigrate/mysql` plugin is used twice, once for the `--storage` option as a <Link href="/plugins/storage/">Storage Plugin</Link>
In the example above the `@emigrate/postgres` plugin is used twice, once for the `--storage` option as a <Link href="/plugins/storage/">Storage Plugin</Link>
and once for the `--plugin` option as a <Link href="/plugins/loaders/">Loader Plugin</Link> to be able to read `.sql` files.
:::

View file

@ -17,6 +17,7 @@ The generator is responsible for generating migration files in a specific format
<CardGrid>
<LinkCard title="JavaScript generator" href="js/" description="A generator that generates .js migration files (using ESM and default export)" />
<LinkCard title="PostgreSQL generator" href="postgres/" description="A generator that generates .sql migration files" />
<LinkCard title="MySQL generator" href="mysql/" description="A generator that generates .sql migration files" />
</CardGrid>

View file

@ -0,0 +1,36 @@
---
title: "PostgreSQL Generator"
---
import { Tabs, TabItem } from '@astrojs/starlight/components';
import Link from '@components/Link.astro';
The PostgreSQL generator creates new migration files with the `.sql` extension. In the same package you can find the <Link href="/plugins/loaders/postgres/">PostgreSQL Loader</Link> and the <Link href="/plugins/storage/postgres/">PostgreSQL Storage</Link>.
## Installation
<Tabs>
<TabItem label="npm">
```bash
npm install @emigrate/postgres
```
</TabItem>
<TabItem label="pnpm">
```bash
pnpm add @emigrate/postgres
```
</TabItem>
<TabItem label="yarn">
```bash
yarn add @emigrate/postgres
```
</TabItem>
</Tabs>
## Usage
```bash
emigrate new --plugin postgres create some fancy table
```
For more information see <Link href="/commands/new/">the `new` command</Link>'s documentation.

View file

@ -21,7 +21,7 @@ Or set it up in your configuration file, see <Link href="/reference/configuratio
:::tip[Did you know?]
You can specify multiple loader plugins at the same time, which is needed when you mix file types in your migrations folder.
For example, you can use the `mysql` loader for `.sql` files and the `typescript` loader for `.ts` files.
For example, you can use the `postgres` or `mysql` loader for `.sql` files and the `typescript` loader for `.ts` files.
The <Link href="/plugins/loaders/default/">default loader</Link> will be used for all other file types, and doesn't need to be specified.
:::
@ -29,5 +29,6 @@ The <Link href="/plugins/loaders/default/">default loader</Link> will be used fo
<CardGrid>
<LinkCard title="Default Loader" href="default/" description="The loader responsible for loading .js, .cjs and .mjs files" />
<LinkCard title="PostgreSQL Loader" href="postgres/" description="Can load and execute .sql files against a PostgreSQL database" />
<LinkCard title="MySQL Loader" href="mysql/" description="Can load and execute .sql files against a MySQL database" />
</CardGrid>

View file

@ -0,0 +1,86 @@
---
title: PostgreSQL Loader Plugin
---
import { Tabs, TabItem } from '@astrojs/starlight/components';
import Link from '@components/Link.astro';
The PostgreSQL loader plugin transforms `.sql` files into JavaScript functions that Emigrate can use to execute the migrations. In the same package you can find the <Link href="/plugins/generators/postgres/">PostgreSQL Generator</Link> and the <Link href="/plugins/storage/postgres/">PostgreSQL Storage</Link>.
## Installation
<Tabs>
<TabItem label="npm">
```bash
npm install @emigrate/postgres
```
</TabItem>
<TabItem label="pnpm">
```bash
pnpm add @emigrate/postgres
```
</TabItem>
<TabItem label="yarn">
```bash
yarn add @emigrate/postgres
```
</TabItem>
</Tabs>
## Configuration
The PostgreSQL loader plugin can be configured either using environment variables or by configuring the plugin directly in the <Link href="/reference/configuration/">`emigrate.config.js` file</Link>.
### Configuration file
```js title="emigrate.config.js" {1,4-8}
import { createPostgresLoader } from '@emigrate/postgres';
export default {
plugins: [
createPostgresLoader({
connection: { ... },
}),
],
};
```
#### Options
##### `connection` (required)
**type:** `object | string`
The connection options to use for connecting to the PostgreSQL database when the SQL statements from the migration files are executed. This can either be a connection URI or an object with connection options.
For a list of supported connection options, see the [postgres documentation](https://github.com/porsager/postgres#connection).
### Environment variables
The following environment variables are supported:
| Variable | Description | Default |
| ------------------- | ----------------------------------------------------------------------------------------------------------- | ------------- |
| `POSTGRES_URL` | The full URI for connecting to a PostgreSQL database, e.g: `"postgres://user:pass@127.0.0.1:3306/database"` | |
| `POSTGRES_HOST` | The host on which the PostgreSQL server instance is running | `"localhost"` |
| `POSTGRES_USER` | The PostgreSQL user account to use for the authentication | |
| `POSTGRES_PASSWORD` | The PostgreSQL user password to use for the authentication | |
| `POSTGRES_PORT` | The network port on which the PostgreSQL server is listening | `5432` |
| `POSTGRES_DB` | The PostgreSQL database to use for the connection | |
:::note
The `POSTGRES_URL` environment variable takes precedence over the other environment variables. If `POSTGRES_URL` is set, the other environment variables are ignored.
:::
The environment variables are used when the plugin is used using the `--plugin` command line option:
```bash
npx emigrate list --plugin postgres
```
Or when specifying the plugin in the <Link href="/reference/configuration/">`emigrate.config.js` file</Link> as a string:
```js title="emigrate.config.js" {2}
export default {
plugins: ['postgres'],
};
```

View file

@ -13,7 +13,7 @@ Usually you'll want to store the migration history in the same database as the o
You can specify a storage plugin via the `--storage` (or `-s` for short) option:
```bash
npx emigrate list --storage mysql
npx emigrate list --storage postgres
```
Or set it up in your configuration file, see <Link href="/reference/configuration/#storage">Storage configuration</Link> for more information.
@ -22,6 +22,7 @@ Or set it up in your configuration file, see <Link href="/reference/configuratio
<CardGrid>
<LinkCard title="File System" href="file-system/" description="The most basic storage plugin - for simple setups" />
<LinkCard title="PostgreSQL" href="postgres/" description="A storage plugin that uses a PostgreSQL database for storing the migration history state" />
<LinkCard title="MySQL" href="mysql/" description="A storage plugin that uses a MySQL database for storing the migration history state" />
</CardGrid>

View file

@ -0,0 +1,93 @@
---
title: PostgreSQL Storage
---
import { Tabs, TabItem } from '@astrojs/starlight/components';
import Link from '@components/Link.astro';
The PostgreSQL storage plugin uses a PostgreSQL database to store the migration history (*duh*). In the same package you can find the <Link href="/plugins/loaders/postgres/">PostgreSQL Loader</Link> and the <Link href="/plugins/generators/postgres/">PostgreSQL Generator</Link>.
## Installation
<Tabs>
<TabItem label="npm">
```bash
npm install @emigrate/postgres
```
</TabItem>
<TabItem label="pnpm">
```bash
pnpm add @emigrate/postgres
```
</TabItem>
<TabItem label="yarn">
```bash
yarn add @emigrate/postgres
```
</TabItem>
</Tabs>
## Configuration
The PostgreSQL storage can be configured either using environment variables or by configuring the plugin directly in the <Link href="/reference/configuration/">`emigrate.config.js` file</Link>.
### Configuration file
```js title="emigrate.config.js" {1,4-7}
import { createPostgresStorage } from '@emigrate/postgres';
export default {
storage: createPostgresStorage({
table: 'migrations',
connection: { ... },
}),
};
```
#### Options
##### `table`
**type:** `string`
**default:** `"migrations"`
The name of the table to use for storing the migrations.
##### `connection` (required)
**type:** `object | string`
The connection options to use for connecting to the PostgreSQL database. This can either be a connection URI or an object with connection options.
For a list of supported connection options, see the [postgres documentation](https://github.com/porsager/postgres#connection).
### Environment variables
The following environment variables are supported:
| Variable | Description | Default |
| ------------------- | ----------------------------------------------------------------------------------------------------------- | -------------- |
| `POSTGRES_TABLE` | The name of the table to use for storing the migrations | `"migrations"` |
| `POSTGRES_URL` | The full URI for connecting to a PostgreSQL database, e.g: `"postgres://user:pass@127.0.0.1:3306/database"` | |
| `POSTGRES_HOST` | The host on which the PostgreSQL server instance is running | `"localhost"` |
| `POSTGRES_USER` | The PostgreSQL user account to use for the authentication | |
| `POSTGRES_PASSWORD` | The PostgreSQL user password to use for the authentication | |
| `POSTGRES_PORT` | The network port on which the PostgreSQL server is listening | `5432` |
| `POSTGRES_DB` | The PostgreSQL database to use for the connection | |
:::note
The `POSTGRES_URL` environment variable takes precedence over the other environment variables. If `POSTGRES_URL` is set, the other environment variables are ignored, except for `POSTGRES_TABLE`.
:::
The environment variables are used when the storage plugin is used using the `--storage` command line option:
```bash
npx emigrate list --storage postgres
```
Or when specifying the storage in the <Link href="/reference/configuration/">`emigrate.config.js` file</Link> as a string:
```js title="emigrate.config.js" {2}
export default {
storage: 'postgres',
};
```

View file

@ -56,7 +56,7 @@ Options:
Examples:
emigrate up --directory src/migrations -s fs
emigrate up -d ./migrations --storage @emigrate/storage-mysql
emigrate up -d ./migrations --storage @emigrate/mysql
emigrate up -d src/migrations -s postgres -r json --dry
`;

View file

@ -1,6 +1,6 @@
# @emigrate/storage-mysql
# @emigrate/mysql
A MySQL plugin for Emigrate. Uses a MySQL database for storing migration history. Can load and generate .sql migration files.
A MySQL plugin for Emigrate. Uses a MySQL database for storing the migration history. Can load and generate .sql migration files.
The table used for storing the migration history is compatible with the [immigration-mysql](https://github.com/joakimbeng/immigration-mysql) package, so you can use this together with the [@emigrate/cli](../cli) as a drop-in replacement for that package.

177
packages/postgres/README.md Normal file
View file

@ -0,0 +1,177 @@
# @emigrate/postgres
A PostgreSQL plugin for Emigrate. Uses a PostgreSQL database for storing the migration history. Can load and generate .sql migration files.
The table used for storing the migration history is compatible with the [immigration-postgres](https://github.com/aboviq/immigration-postgres) package, so you can use this together with the [@emigrate/cli](../cli) as a drop-in replacement for that package.
## Description
This plugin is actually three different Emigrate plugins in one:
1. A [storage plugin](#using-the-storage-plugin) for storing the migration history in a PostgreSQL database.
2. A [loader plugin](#using-the-loader-plugin) for loading .sql migration files and be able to execute them as part of the migration process.
3. A [generator plugin](#using-the-generator-plugin) for generating .sql migration files.
## Installation
Install the plugin in your project, alongside the Emigrate CLI:
```bash
npm install --save-dev @emigrate/cli @emigrate/postgres
```
## Usage
### Using the storage plugin
See [Options](#options) below for the default values and how to configure the plugin using environment variables.
Configure the storage in your `emigrate.config.js` file:
```js
export default {
directory: 'migrations',
storage: 'postgres', // the @emigrate/ prefix is optional
};
```
Or use the CLI options `--storage` (or `-s`)
```bash
emigrate up --storage postgres # the @emigrate/ prefix is optional
```
#### Storage plugin with custom options
Configure the storage in your `emigrate.config.js` file by importing the `createPostgresStorage` function (see [Options](#options) for available options).
In this mode the plugin will _not_ use any of the environment variables for configuration.
```js
import { createPostgresStorage } from '@emigrate/postgres';
export default {
directory: 'migrations',
storage: createPostgresStorage({ table: 'migrations', connection: { ... } }), // All connection options are passed to postgres()
};
```
Or use the CLI option `--storage` (or `-s`) and use environment variables (see [Options](#options) for available variables).
```bash
POSTGRES_URL=postgres://user:pass@host/db emigrate up --storage postgres # the @emigrate/ prefix is optional
```
### Using the loader plugin
The loader plugin is used to transform .sql migration files into JavaScript functions that can be executed by the "up" command.
See [Options](#options) below for the default values and how to configure the plugin using environment variables.
Configure the loader in your `emigrate.config.js` file:
```js
export default {
directory: 'migrations',
plugins: ['postgres'], // the @emigrate/ prefix is optional
};
```
Or by importing the default export from the plugin:
```js
import postgresPlugin from '@emigrate/postgres';
export default {
directory: 'migrations',
plugins: [postgresPlugin],
};
```
**NOTE:** Using the root level `plugins` option will load the plugin for all commands, which means the [generator plugin](#using-the-generator-plugin) will be used by default for the "new" command as well. If you only want to use the loader plugin, use the `up.plugins` option instead:
```js
export default {
directory: 'migrations',
up: {
plugins: ['postgres'], // the @emigrate/ prefix is optional
// or:
plugins: [import('@emigrate/postgres')],
},
};
```
The loader plugin can also be loaded using the CLI option `--plugin` (or `-p`) together with the "up" command:
```bash
emigrate up --plugin postgres # the @emigrate/ prefix is optional
```
### Using the generator plugin
The generator plugin is used to generate skeleton .sql migration files inside your migration directory.
Configure the generator in your `emigrate.config.js` file:
```js
export default {
directory: 'migrations',
plugins: ['postgres'], // the @emigrate/ prefix is optional
};
```
Or by importing the default export from the plugin:
```js
import postgresPlugin from '@emigrate/postgres';
export default {
directory: 'migrations',
plugins: [postgresPlugin],
};
```
**NOTE:** Using the root level `plugins` option will load the plugin for all commands, which means the [loader plugin](#using-the-loader-plugin) will be used by default for the "up" command as well. If you only want to use the generator plugin, use the `new.plugins` option instead:
```js
export default {
directory: 'migrations',
new: {
plugins: ['postgres'], // the @emigrate/ prefix is optional
// or:
plugins: [import('@emigrate/postgres')],
},
};
```
The generator plugin can also be loaded using the CLI option `--plugin` (or `-p`) together with the "new" command:
```bash
emigrate new --plugin postgres My new migration file # the @emigrate/ prefix is optional
```
#### Loader plugin with custom options
Configure the loader in your `emigrate.config.js` file by importing the `createPostgresLoader` function (see [Options](#options) for available options).
In this mode the plugin will _not_ use any of the environment variables for configuration.
```js
import { createPostgresLoader } from '@emigrate/postgres';
export default {
directory: 'migrations',
plugins: [
createPostgresLoader({ connection: { ... } }), // All connection options are passed to postgres()
],
};
```
## Options
The storage plugin accepts the following options:
| Option | Applies to | Description | Default | Environment variable |
| ------------ | -------------------------- | -------------------------------------------------------------------------------------------------- | ------------ | ---------------------------------------------------------------------------------------------------------- |
| `table` | storage plugin | The name of the table to use for storing the migrations. | `migrations` | `POSTGRES_TABLE` |
| `connection` | storage and loader plugins | The connection options to pass to [`postgres()`](https://github.com/porsager/postgres#connection). | `{}` | `POSTGRES_URL` or `POSTGRES_HOST`, `POSTGRES_PORT`, `POSTGRES_USER`, `POSTGRES_PASSWORD` and `POSTGRES_DB` |

View file

@ -0,0 +1,51 @@
{
"name": "@emigrate/postgres",
"version": "0.0.0",
"publishConfig": {
"access": "public"
},
"description": "A PostgreSQL plugin for Emigrate. Uses a PostgreSQL database for storing migration history. Can load and generate .sql migration files.",
"main": "dist/index.js",
"types": "dist/index.d.js",
"type": "module",
"exports": {
".": {
"import": "./dist/index.js",
"types": "./dist/index.d.ts"
}
},
"files": [
"dist"
],
"scripts": {
"build": "tsc --pretty",
"build:watch": "tsc --pretty --watch",
"lint": "xo --cwd=../.. $(pwd)"
},
"keywords": [
"emigrate",
"emigrate-storage",
"emigrate-loader",
"emigrate-plugin",
"emigrate-generator",
"migrations",
"postgres",
"postgresql"
],
"author": "Aboviq AB <dev@aboviq.com> (https://www.aboviq.com)",
"homepage": "https://github.com/aboviq/emigrate/tree/main/packages/postgres#readme",
"repository": "https://github.com/aboviq/emigrate/tree/main/packages/postgres",
"bugs": "https://github.com/aboviq/emigrate/issues",
"license": "MIT",
"dependencies": {
"@emigrate/plugin-tools": "workspace:*",
"@emigrate/types": "workspace:*",
"postgres": "3.4.3"
},
"devDependencies": {
"@emigrate/tsconfig": "workspace:*"
},
"volta": {
"extends": "../../package.json"
}
}

View file

@ -0,0 +1,252 @@
import process from 'node:process';
import postgres, { type Options, type PostgresType, type Sql } from 'postgres';
import { getTimestampPrefix, sanitizeMigrationName } from '@emigrate/plugin-tools';
import {
type MigrationMetadata,
type EmigrateStorage,
type LoaderPlugin,
type Storage,
type MigrationMetadataFinished,
type GenerateMigrationFunction,
type GeneratorPlugin,
type SerializedError,
type MigrationHistoryEntry,
} from '@emigrate/types';
const defaultTable = 'migrations';
type ConnectionOptions = Options<Record<string, PostgresType>>;
export type PostgresStorageOptions = {
table?: string;
/**
* @see https://github.com/porsager/postgres#connection
*/
connection: ConnectionOptions | string;
};
export type PostgresLoaderOptions = {
/**
* @see https://github.com/porsager/postgres#connection
*/
connection: ConnectionOptions | string;
};
const getPool = (connection: ConnectionOptions | string) => {
if (typeof connection === 'string') {
return postgres(connection);
}
return postgres(connection);
};
const lockMigration = async (sql: Sql, table: string, migration: MigrationMetadata) => {
const result = await sql`
INSERT INTO ${sql(table)} (name, status, date)
VALUES (${migration.name}, ${'locked'}, NOW())
ON CONFLICT (name) DO NOTHING
`;
return result.count === 1;
};
const unlockMigration = async (sql: Sql, table: string, migration: MigrationMetadata) => {
const result = await sql`
DELETE FROM ${sql(table)}
WHERE
name = ${migration.name}
AND status = ${'locked'}
`;
return result.count === 1;
};
const finishMigration = async (
sql: Sql,
table: string,
migration: MigrationMetadataFinished,
_error?: SerializedError,
) => {
const result = await sql`
UPDATE
${sql(table)}
SET
status = ${migration.status},
date = NOW()
WHERE
name = ${migration.name}
AND status = ${'locked'}
`;
return result.count === 1;
};
const deleteMigration = async (sql: Sql, table: string, migration: MigrationMetadata) => {
const result = await sql`
DELETE FROM ${sql(table)}
WHERE
name = ${migration.name}
AND status <> ${'locked'}
`;
return result.count === 1;
};
const initializeTable = async (sql: Sql, table: string) => {
const [row] = await sql<Array<{ exists: 1 }>>`
SELECT 1 as exists
FROM
information_schema.tables
WHERE
table_schema = 'public'
AND table_name = ${table}
`;
if (row?.exists) {
return;
}
// This table definition is compatible with the one used by the immigration-postgres package
await sql`
CREATE TABLE ${sql(table)} (
name varchar(255) not null primary key,
status varchar(32),
date timestamptz not null
);
`;
};
export const createPostgresStorage = ({
table = defaultTable,
connection,
}: PostgresStorageOptions): EmigrateStorage => {
return {
async initializeStorage() {
const sql = getPool(connection);
try {
await initializeTable(sql, table);
} catch (error) {
await sql.end();
throw error;
}
const storage: Storage = {
async lock(migrations) {
const lockedMigrations: MigrationMetadata[] = [];
for await (const migration of migrations) {
if (await lockMigration(sql, table, migration)) {
lockedMigrations.push(migration);
}
}
return lockedMigrations;
},
async unlock(migrations) {
for await (const migration of migrations) {
await unlockMigration(sql, table, migration);
}
},
async remove(migration) {
await deleteMigration(sql, table, migration);
},
async *getHistory() {
const query = sql<Array<Exclude<MigrationHistoryEntry, 'error'>>>`
SELECT
*
FROM
${sql(table)}
WHERE
status <> ${'locked'}
ORDER BY
date ASC
`.cursor();
for await (const [row] of query) {
if (!row) {
continue;
}
if (row.status === 'failed') {
yield {
...row,
error: { name: 'Error', message: 'Unknown error' },
};
continue;
}
yield row;
}
},
async onSuccess(migration) {
await finishMigration(sql, table, migration);
},
async onError(migration, error) {
await finishMigration(sql, table, migration, error);
},
async end() {
await sql.end();
},
};
return storage;
},
};
};
export const { initializeStorage } = createPostgresStorage({
table: process.env['POSTGRES_TABLE'],
connection: process.env['POSTGRES_URL'] ?? {
host: process.env['POSTGRES_HOST'],
port: process.env['POSTGRES_PORT'] ? Number.parseInt(process.env['POSTGRES_PORT'], 10) : undefined,
user: process.env['POSTGRES_USER'],
password: process.env['POSTGRES_PASSWORD'],
database: process.env['POSTGRES_DB'],
},
});
export const createPostgresLoader = ({ connection }: PostgresLoaderOptions): LoaderPlugin => {
return {
loadableExtensions: ['.sql'],
async loadMigration(migration) {
return async () => {
const sql = getPool(connection);
try {
// @ts-expect-error The "simple" option is not documented, but it exists
await sql.file(migration.filePath, { simple: true });
} finally {
await sql.end();
}
};
},
};
};
export const { loadableExtensions, loadMigration } = createPostgresLoader({
connection: process.env['POSTGRES_URL'] ?? {
host: process.env['POSTGRES_HOST'],
port: process.env['POSTGRES_PORT'] ? Number.parseInt(process.env['POSTGRES_PORT'], 10) : undefined,
user: process.env['POSTGRES_USER'],
password: process.env['POSTGRES_PASSWORD'],
database: process.env['POSTGRES_DB'],
},
});
export const generateMigration: GenerateMigrationFunction = async (name) => {
return {
filename: `${getTimestampPrefix()}_${sanitizeMigrationName(name)}.sql`,
content: `-- Migration: ${name}
`,
};
};
const defaultExport: EmigrateStorage & LoaderPlugin & GeneratorPlugin = {
initializeStorage,
loadableExtensions,
loadMigration,
generateMigration,
};
export default defaultExport;

View file

@ -0,0 +1,8 @@
{
"extends": "@emigrate/tsconfig/build.json",
"compilerOptions": {
"outDir": "dist"
},
"include": ["src"],
"exclude": ["node_modules", "dist"]
}

21
pnpm-lock.yaml generated
View file

@ -148,6 +148,22 @@ importers:
specifier: workspace:*
version: link:../tsconfig
packages/postgres:
dependencies:
'@emigrate/plugin-tools':
specifier: workspace:*
version: link:../plugin-tools
'@emigrate/types':
specifier: workspace:*
version: link:../types
postgres:
specifier: 3.4.3
version: 3.4.3
devDependencies:
'@emigrate/tsconfig':
specifier: workspace:*
version: link:../tsconfig
packages/reporter-pino:
dependencies:
'@emigrate/types':
@ -7030,6 +7046,11 @@ packages:
source-map-js: 1.0.2
dev: false
/postgres@3.4.3:
resolution: {integrity: sha512-iHJn4+M9vbTdHSdDzNkC0crHq+1CUdFhx+YqCE+SqWxPjm+Zu63jq7yZborOBF64c8pc58O5uMudyL1FQcHacA==}
engines: {node: '>=12'}
dev: false
/prebuild-install@7.1.1:
resolution: {integrity: sha512-jAXscXWMcCK8GgCoHOfIr0ODh5ai8mj63L2nWrjuAgXE6tDyYGnx4/8o/rCgU+B4JSyZBKbeZqzhtwtC3ovxjw==}
engines: {node: '>=10'}