rake-db
Version:
Migrations tool for Postgresql DB
1,359 lines (1,352 loc) • 63.3 kB
TypeScript
import * as pqb from 'pqb';
import { ColumnsShape, Db, TableData, NoPrimaryKeyOption, ColumnType, EnumColumn, DefaultColumnTypes, DefaultSchemaConfig, AdapterOptions, DbResult, Adapter, TransactionAdapter, TableDataFn, TableDataItem, DbDomainArg, raw, SearchWeight, ColumnsByType, DbStructureDomainsMap } from 'pqb';
import * as orchid_core from 'orchid-core';
import { MaybeArray, RawSQLBase, ColumnDataCheckBase, RecordString, ColumnTypeBase, EmptyObject, ColumnSchemaConfig, QueryLogOptions, MaybePromise, QueryLogObject, QueryBase } from 'orchid-core';
interface CreateTableResult<Table extends string, Shape extends ColumnsShape> {
table: Db<Table, Shape>;
}
type RakeDbAst = RakeDbAst.Table | RakeDbAst.ChangeTable | RakeDbAst.RenameType | RakeDbAst.Schema | RakeDbAst.RenameSchema | RakeDbAst.Extension | RakeDbAst.Enum | RakeDbAst.EnumValues | RakeDbAst.RenameEnumValues | RakeDbAst.ChangeEnumValues | RakeDbAst.Domain | RakeDbAst.Collation | RakeDbAst.Constraint | RakeDbAst.RenameTableItem | RakeDbAst.View;
declare namespace RakeDbAst {
interface Table extends TableData {
type: 'table';
action: 'create' | 'drop';
schema?: string;
name: string;
shape: ColumnsShape;
noPrimaryKey: NoPrimaryKeyOption;
createIfNotExists?: boolean;
dropIfExists?: boolean;
dropMode?: DropMode;
comment?: string;
}
interface ChangeTable {
type: 'changeTable';
schema?: string;
name: string;
comment?: string | [string, string] | null;
shape: ChangeTableShape;
add: TableData;
drop: TableData;
}
type ChangeTableShape = Record<string, MaybeArray<ChangeTableItem>>;
type ChangeTableItem = ChangeTableItem.Column | ChangeTableItem.Change | ChangeTableItem.Rename;
namespace ChangeTableItem {
interface Column {
type: 'add' | 'drop';
item: ColumnType;
dropMode?: DropMode;
}
interface Change {
type: 'change';
name?: string;
from: ColumnChange;
to: ColumnChange;
using?: ChangeUsing;
}
interface ChangeUsing {
usingUp?: RawSQLBase;
usingDown?: RawSQLBase;
}
interface Rename {
type: 'rename';
name: string;
}
}
interface ColumnChange {
column?: ColumnType;
type?: string;
collate?: string;
default?: unknown | RawSQLBase;
nullable?: boolean;
comment?: string | null;
compression?: string;
primaryKey?: boolean;
checks?: ColumnDataCheckBase[];
foreignKeys?: TableData.ColumnReferences[];
indexes?: TableData.ColumnIndex[];
excludes?: TableData.ColumnExclude[];
identity?: TableData.Identity;
}
interface RenameType {
type: 'renameType';
kind: 'TABLE' | 'TYPE' | 'DOMAIN';
fromSchema?: string;
from: string;
toSchema?: string;
to: string;
}
interface Schema {
type: 'schema';
action: 'create' | 'drop';
name: string;
}
interface RenameSchema {
type: 'renameSchema';
from: string;
to: string;
}
interface ExtensionArg {
version?: string;
cascade?: boolean;
createIfNotExists?: boolean;
dropIfExists?: boolean;
}
interface Extension extends ExtensionArg {
type: 'extension';
action: 'create' | 'drop';
schema?: string;
name: string;
}
interface Enum {
type: 'enum';
action: 'create' | 'drop';
schema?: string;
name: string;
values: [string, ...string[]];
cascade?: boolean;
dropIfExists?: boolean;
}
interface EnumValues {
type: 'enumValues';
action: 'add' | 'drop';
schema?: string;
name: string;
values: string[];
place?: 'before' | 'after';
relativeTo?: string;
ifNotExists?: boolean;
}
interface RenameEnumValues {
type: 'renameEnumValues';
schema?: string;
name: string;
values: RecordString;
}
interface ChangeEnumValues {
type: 'changeEnumValues';
schema?: string;
name: string;
fromValues: string[];
toValues: string[];
}
interface Domain {
type: 'domain';
action: 'create' | 'drop';
schema?: string;
name: string;
baseType: ColumnType;
}
interface Collation {
type: 'collation';
action: 'create' | 'drop';
schema?: string;
name: string;
locale?: string;
lcCollate?: string;
lcCType?: string;
provider?: string;
deterministic?: boolean;
version?: string;
fromExisting?: string;
createIfNotExists?: boolean;
dropIfExists?: boolean;
cascade?: boolean;
}
interface EnumOptions {
createIfNotExists?: boolean;
dropIfExists?: boolean;
}
interface Constraint extends TableData.Constraint {
type: 'constraint';
action: 'create' | 'drop';
tableSchema?: string;
tableName: string;
}
interface RenameTableItem {
type: 'renameTableItem';
kind: 'INDEX' | 'CONSTRAINT';
tableSchema?: string;
tableName: string;
from: string;
to: string;
}
interface View {
type: 'view';
action: 'create' | 'drop';
schema?: string;
name: string;
shape: ColumnsShape;
sql: RawSQLBase;
options: ViewOptions;
deps: {
schemaName: string;
name: string;
}[];
}
interface ViewOptions {
createOrReplace?: boolean;
dropIfExists?: boolean;
dropMode?: DropMode;
temporary?: boolean;
recursive?: boolean;
columns?: string[];
with?: {
checkOption?: 'LOCAL' | 'CASCADED';
securityBarrier?: boolean;
securityInvoker?: boolean;
};
}
}
declare function add(item: ColumnType, options?: {
dropMode?: DropMode;
}): SpecialChange;
declare function add(emptyObject: EmptyObject): SpecialChange;
declare function add(items: Record<string, ColumnType>, options?: {
dropMode?: DropMode;
}): Record<string, RakeDbAst.ChangeTableItem.Column>;
interface Change extends RakeDbAst.ChangeTableItem.Change, ChangeOptions {
}
type ChangeOptions = RakeDbAst.ChangeTableItem.ChangeUsing;
interface SpecialChange {
type: SpecialChange;
}
interface OneWayChange {
type: 'change';
name?: string;
to: RakeDbAst.ColumnChange;
using?: RakeDbAst.ChangeTableItem.ChangeUsing;
}
type TableChangeMethods = typeof tableChangeMethods;
declare const tableChangeMethods: {
name(name: string): any;
add: typeof add;
drop: typeof add;
change(from: ColumnType | OneWayChange, to: ColumnType | OneWayChange, using?: ChangeOptions): Change;
default(value: unknown | RawSQLBase): OneWayChange;
nullable(): OneWayChange;
nonNullable(): OneWayChange;
comment(comment: string | null): OneWayChange;
/**
* Rename a column:
*
* ```ts
* import { change } from '../dbScript';
*
* change(async (db) => {
* await db.changeTable('table', (t) => ({
* oldColumnName: t.rename('newColumnName'),
* }));
* });
* ```
*
* Note that the renaming `ALTER TABLE` is executed before the rest of alterations,
* so if you're also adding a new constraint on this column inside the same `changeTable`,
* refer to it with a new name.
*
* @param name
*/
rename(name: string): RakeDbAst.ChangeTableItem.Rename;
primaryKey<Columns extends [string, ...string[]], Name extends string>(columns: Columns, name?: Name | undefined): {
tableDataItem: true;
columns: Columns;
name: string extends Name ? never : Name;
};
unique<Columns_1 extends [string | TableData.Index.ColumnOrExpressionOptions<string>, ...(string | TableData.Index.ColumnOrExpressionOptions<string>)[]], Name_1 extends string>(columns: Columns_1, ...args: [options?: TableData.Index.UniqueOptionsArg | undefined] | [name?: Name_1 | undefined, options?: TableData.Index.UniqueOptionsArg | undefined]): {
tableDataItem: true;
columns: Columns_1 extends (string | TableData.Index.ColumnOptionsForColumn<string>)[] ? { [I in keyof Columns_1]: "column" extends keyof Columns_1[I] ? Columns_1[I][keyof Columns_1[I] & "column"] : Columns_1[I]; } : never;
name: string extends Name_1 ? never : Name_1;
};
index(columns: (string | TableData.Index.ColumnOrExpressionOptions<string>)[], ...args: [options?: TableData.Index.OptionsArg | undefined] | [name?: string | undefined, options?: TableData.Index.OptionsArg | undefined]): pqb.NonUniqDataItem;
searchIndex(columns: (string | TableData.Index.ColumnOrExpressionOptions<string>)[], ...args: [options?: TableData.Index.TsVectorArg | undefined] | [name?: string | undefined, options?: TableData.Index.TsVectorArg | undefined]): pqb.NonUniqDataItem;
exclude(columns: TableData.Exclude.ColumnOrExpressionOptions<string>[], ...args: [options?: TableData.Exclude.Options | undefined] | [name?: string | undefined, options?: TableData.Exclude.Options | undefined]): pqb.NonUniqDataItem;
foreignKey<ForeignTable extends string | (() => orchid_core.ForeignKeyTable), ForeignColumns extends ForeignTable extends () => orchid_core.ForeignKeyTable ? [orchid_core.ColumnNameOfTable<ReturnType<ForeignTable>>, ...orchid_core.ColumnNameOfTable<ReturnType<ForeignTable>>[]] : [string, ...string[]]>(columns: [string, ...string[]], fnOrTable: ForeignTable, foreignColumns: ForeignColumns, options?: TableData.References.Options | undefined): pqb.NonUniqDataItem;
check(check: RawSQLBase<orchid_core.QueryColumn<unknown, any>, unknown>, name?: string | undefined): pqb.NonUniqDataItem;
sql: pqb.SqlFn;
enum(name: string): EnumColumn<pqb.DefaultSchemaConfig, undefined, [string, ...string[]]>;
};
type TableChanger<CT> = MigrationColumnTypes<CT> & TableChangeMethods;
type TableChangeData = Record<string, RakeDbAst.ChangeTableItem.Column | RakeDbAst.ChangeTableItem.Rename | Change | SpecialChange | ColumnTypeBase>;
interface RakeDbCtx {
migrationsPromise?: Promise<MigrationsSet>;
}
declare const getSchemaAndTableFromName: (name: string) => [string | undefined, string];
declare const concatSchemaAndName: ({ schema, name, }: {
schema?: string;
name: string;
}) => string;
interface MigrationItemHasLoad {
path?: string;
/**
* Function that loads the migration content,
* can store lazy import of a migration file.
* Promise can return `{ default: x }` where `x` is a return of `change` or an array of such returns.
*/
load(): Promise<unknown>;
}
interface MigrationItem extends MigrationItemHasLoad {
path: string;
version: string;
}
interface MigrationsSet {
renameTo?: RakeDbRenameMigrations;
migrations: MigrationItem[];
}
interface CommandFn<SchemaConfig extends ColumnSchemaConfig, CT> {
(options: AdapterOptions[], config: RakeDbConfig<SchemaConfig, CT>, args: string[]): void | Promise<void>;
}
interface RakeDbBaseConfig<SchemaConfig extends ColumnSchemaConfig, CT = DefaultColumnTypes<DefaultSchemaConfig>> extends QueryLogOptions {
schemaConfig: SchemaConfig;
migrationsPath: string;
migrationId: RakeDbMigrationId;
migrations?: ModuleExportsRecord;
renameMigrations?: RakeDbRenameMigrationsInput;
migrationsTable: string;
snakeCase: boolean;
language?: string;
commands: Record<string, CommandFn<SchemaConfig, CT>>;
noPrimaryKey?: NoPrimaryKeyOption;
baseTable?: RakeDbBaseTable<CT>;
forceDefaultExports?: boolean;
import(path: string): Promise<unknown>;
beforeChange?: ChangeCallback$1;
afterChange?: ChangeCallback$1;
afterChangeCommit?: ChangeCommitCallback;
beforeMigrate?: MigrationCallback;
afterMigrate?: MigrationCallback;
beforeRollback?: MigrationCallback;
afterRollback?: MigrationCallback;
}
interface RakeDbConfig<SchemaConfig extends ColumnSchemaConfig, CT = DefaultColumnTypes<DefaultSchemaConfig>> extends RakeDbBaseConfig<SchemaConfig, CT> {
columnTypes: CT;
basePath: string;
dbScript: string;
recurrentPath: string;
}
interface InputRakeDbConfigBase<SchemaConfig extends ColumnSchemaConfig, CT> extends QueryLogOptions {
columnTypes?: CT | ((t: DefaultColumnTypes<DefaultSchemaConfig>) => CT);
baseTable?: RakeDbBaseTable<CT>;
schemaConfig?: SchemaConfig;
basePath?: string;
dbScript?: string;
migrationsPath?: string;
migrationId?: 'serial' | RakeDbMigrationId;
recurrentPath?: string;
migrationsTable?: string;
snakeCase?: boolean;
language?: string;
commands?: Record<string, (options: AdapterOptions[], config: RakeDbConfig<SchemaConfig, CT>, args: string[]) => void | Promise<void>>;
noPrimaryKey?: NoPrimaryKeyOption;
forceDefaultExports?: boolean;
/**
* Is called once per db before migrating or rolling back a set of migrations.
*
* @param arg.db - query builder
* @param arg.up - whether it's migrating up or down
* @param arg.redo - whether it's migrating down and then up for `redo` command
* @param arg.migrations - array of executed (up or down) migrations
*/
beforeChange?: ChangeCallback$1;
/**
* Is called once per db after migrating or rolling back a set of migrations.
* Runs inside the same transaction as migrations,
* for running after commit use {@link afterChangeCommit}.
*
* @param arg.db - query builder
* @param arg.up - whether it's migrating up or down
* @param arg.redo - whether it's migrating down and then up for `redo` command
* @param arg.migrations - array of executed (up or down) migrations
*/
afterChange?: ChangeCallback$1;
/**
* Is called once per db after migrating or rolling back a set of migrations.
* Runs **after** committing migrations transaction.
*
* @param arg.options - database connection options
* @param arg.up - whether it's migrating up or down
* @param arg.migrations - array of executed (up or down) migrations
*/
afterChangeCommit?: ChangeCommitCallback;
/**
* Is called once per db before migrating (up) a set of migrations.
*
* @param arg.db - query builder
* @param arg.migrations - applied migrations
*/
beforeMigrate?: MigrationCallback;
/**
* Is called once per db after migrating (up) a set of migrations.
*
* @param arg.db - query builder
* @param arg.migrations - applied migrations
*/
afterMigrate?: MigrationCallback;
/**
* Is called once per db before rolling back a set of migrations.
*
* @param arg.db - query builder
* @param arg.migrations - rolled back migrations
*/
beforeRollback?: MigrationCallback;
/**
* Is called once per db before rolling back a set of migrations.
*
* @param arg.db - query builder
* @param arg.migrations - rolled back migrations
*/
afterRollback?: MigrationCallback;
}
interface InputRakeDbConfigFileBased<SchemaConfig extends ColumnSchemaConfig, CT> extends InputRakeDbConfigBase<SchemaConfig, CT> {
/**
* It may look odd, but it's required for `tsx` and other bundlers to have such `import` config specified explicitly.
*/
import(path: string): Promise<unknown>;
}
interface InputRakeDbConfigCodeBased<SchemaConfig extends ColumnSchemaConfig, CT> extends InputRakeDbConfigBase<SchemaConfig, CT> {
/**
* To specify array of migrations explicitly, without loading them from files.
*/
migrations: ModuleExportsRecord;
renameMigrations?: RakeDbRenameMigrationsInput;
/**
* It may look odd, but it's required for `tsx` and other bundlers to have such `import` config specified explicitly.
*/
import?(path: string): Promise<unknown>;
}
type InputRakeDbConfig<SchemaConfig extends ColumnSchemaConfig, CT> = InputRakeDbConfigFileBased<SchemaConfig, CT> | InputRakeDbConfigCodeBased<SchemaConfig, CT>;
interface ChangeCallback$1 {
(arg: {
db: DbResult<unknown>;
up: boolean;
redo: boolean;
migrations: MigrationItem[];
}): void | Promise<void>;
}
interface ChangeCommitCallback {
(arg: {
options: AdapterOptions;
up: boolean;
migrations: MigrationItem[];
}): void | Promise<void>;
}
interface MigrationCallback {
(arg: {
db: DbResult<unknown>;
migrations: MigrationItem[];
}): void | Promise<void>;
}
type AnyRakeDbConfig = RakeDbConfig<any, any>;
interface RakeDbBaseTable<CT> {
exportAs: string;
getFilePath(): string;
nowSQL?: string;
new (): {
types: CT;
snakeCase?: boolean;
language?: string;
};
}
interface ModuleExportsRecord {
[K: string]: () => Promise<unknown>;
}
type RakeDbMigrationId = 'timestamp' | {
serial: number;
};
interface RakeDbRenameMigrationsMap {
[K: string]: number;
}
interface RakeDbRenameMigrations {
to: RakeDbMigrationId;
map(): MaybePromise<RakeDbRenameMigrationsMap>;
}
interface RakeDbRenameMigrationsInput {
to: RakeDbMigrationId;
map: RakeDbRenameMigrationsMap;
}
declare const migrationConfigDefaults: RakeDbBaseConfig<ColumnSchemaConfig>;
type DropMode = 'CASCADE' | 'RESTRICT';
type TableOptions = {
createIfNotExists?: boolean;
dropIfExists?: boolean;
dropMode?: DropMode;
comment?: string;
noPrimaryKey?: boolean;
snakeCase?: boolean;
language?: string;
};
type MigrationColumnTypes<CT> = Omit<CT, 'enum'> & {
enum: (name: string) => EnumColumn<ColumnSchemaConfig, unknown, readonly string[]>;
};
type ColumnsShapeCallback<CT, Shape extends ColumnsShape = ColumnsShape> = (t: MigrationColumnTypes<CT> & {
raw: typeof raw;
}) => Shape;
type ChangeTableOptions = {
snakeCase?: boolean;
language?: string;
comment?: string | [string, string] | null;
};
type ChangeTableCallback<CT> = (t: TableChanger<CT>) => TableChangeData;
type SilentQueries = {
silentQuery: Adapter['query'];
silentArrays: Adapter['arrays'];
};
type DbMigration<CT> = DbResult<CT> & Migration<CT> & {
adapter: SilentQueries;
};
/**
* Creates a new `db` instance that is an instance of `pqb` with mixed in migration methods from the `Migration` class.
* It overrides `query` and `array` db adapter methods to intercept SQL for the logging.
*
* @param tx - database adapter that executes inside a transaction
* @param up - migrate or rollback
* @param config - config of `rakeDb`
*/
declare const createMigrationInterface: <SchemaConfig extends ColumnSchemaConfig<orchid_core.ColumnTypeBase<orchid_core.ColumnTypeSchemaArg, unknown, any, any, unknown, unknown, any, unknown, any, orchid_core.ColumnDataBase>>, CT>(tx: TransactionAdapter, up: boolean, config: RakeDbConfig<SchemaConfig, CT>) => DbMigration<CT>;
interface MigrationAdapter extends TransactionAdapter {
schema: string;
}
declare class Migration<CT> {
adapter: MigrationAdapter;
log?: QueryLogObject;
up: boolean;
options: RakeDbConfig<ColumnSchemaConfig>;
columnTypes: CT;
/**
* `createTable` accepts a string for a table name, optional options, and a callback to specify columns.
*
* `dropTable` accepts the same arguments, it will drop the table when migrating and create a table when rolling back.
*
* To create an empty table, the callback with columns may be omitted.
*
* When creating a table within a specific schema, write the table name with schema name: `'schemaName.tableName'`.
*
* Returns object `{ table: TableInterface }` that allows to insert records right after creating a table.
*
* Options are:
*
* ```ts
* type TableOptions = {
* // create the table only if it not exists already
* createIfNotExists?: boolean;
*
* // drop the table only if it exists
* dropIfExists?: boolean;
*
* // used when reverting a `createTable`
* dropMode?: 'CASCADE' | 'RESTRICT';
*
* // add a database comment on the table
* comment?: string;
*
* // by default, it will throw an error when the table has no primary key
* // set `noPrimaryKey` to `true` to bypass it
* noPrimaryKey?: boolean;
*
* // override rakeDb `snakeCase` option for only this table
* snakeCase?: boolean;
* };
* ```
*
* Example:
*
* ```ts
* import { change } from '../dbScript';
*
* change(async (db, up) => {
* // call `createTable` with options
* await db.createTable(
* 'table',
* {
* comment: 'Table comment',
* dropMode: 'CASCADE',
* noPrimaryKey: true,
* },
* (t) => ({
* // ...
* }),
* );
*
* // call without options
* const { table } = await db.createTable('user', (t) => ({
* id: t.identity().primaryKey(),
* email: t.text().unique(),
* name: t.text(),
* active: t.boolean().nullable(),
* ...t.timestamps(),
* }));
*
* // create records only when migrating up
* if (up) {
* // table is a db table interface, all query methods are available
* await table.createMany([...data]);
* }
* });
* ```
*
* @param tableName - name of the table to create
* @param fn - create table callback
* @param dataFn - callback for creating composite indexes, primary keys, foreign keys
*/
createTable<Table extends string, Shape extends ColumnsShape>(tableName: Table, fn?: ColumnsShapeCallback<CT, Shape>, dataFn?: TableDataFn<Shape, MaybeArray<TableDataItem>>): Promise<CreateTableResult<Table, Shape>>;
/**
* See {@link createTable}
*
* @param tableName - name of the table to create
* @param options - {@link TableOptions}
* @param fn - create table callback
* @param dataFn - callback for creating composite indexes, primary keys, foreign keys
*/
createTable<Table extends string, Shape extends ColumnsShape>(tableName: Table, options: TableOptions, fn?: ColumnsShapeCallback<CT, Shape>, dataFn?: TableDataFn<Shape, MaybeArray<TableDataItem>>): Promise<CreateTableResult<Table, Shape>>;
/**
* Drop the table, create it on rollback. See {@link createTable}.
*
* @param tableName - name of the table to drop
* @param fn - create table callback
* @param dataFn - callback for creating composite indexes, primary keys, foreign keys
*/
dropTable<Table extends string, Shape extends ColumnsShape>(tableName: Table, fn?: ColumnsShapeCallback<CT, Shape>, dataFn?: TableDataFn<Shape, MaybeArray<TableDataItem>>): Promise<CreateTableResult<Table, Shape>>;
/**
* Drop the table, create it on rollback. See {@link createTable}.
*
* @param tableName - name of the table to drop
* @param options - {@link TableOptions}
* @param fn - create table callback
* @param dataFn - callback for creating composite indexes, primary keys, foreign keys
*/
dropTable<Table extends string, Shape extends ColumnsShape>(tableName: Table, options: TableOptions, fn?: ColumnsShapeCallback<CT, Shape>, dataFn?: TableDataFn<Shape, MaybeArray<TableDataItem>>): Promise<CreateTableResult<Table, Shape>>;
/**
* `changeTable` accepts a table name, optional options, and a special callback with column changes.
*
* When changing a table within a specific schema, write the table name with schema name: `'schemaName.tableName'`.
*
* Options are:
*
* ```ts
* type ChangeTableOptions = {
* comment?:
* | // add a comment to the table on migrating, remove a comment on rollback
* string // change comment from first to second on migrating, from second to first on rollback
* | [string, string] // remove a comment on both migrate and rollback
* | null;
*
* // override rakeDb `snakeCase` option for only this table
* snakeCase?: boolean;
* };
* ```
*
* The callback of the `changeTable` is different from `createTable` in the way that it expects columns to be wrapped in change methods such as `add`, `drop`, and `change`.
*
* @param tableName - name of the table to change (ALTER)
* @param fn - change table callback
*/
changeTable(tableName: string, fn: ChangeTableCallback<CT>): Promise<void>;
/**
* See {@link changeTable}
*
* @param tableName - name of the table to change (ALTER)
* @param options - change table options
* @param fn - change table callback
*/
changeTable(tableName: string, options: ChangeTableOptions, fn?: ChangeTableCallback<CT>): Promise<void>;
/**
* Rename a table:
*
* ```ts
* import { change } from '../dbScript';
*
* change(async (db) => {
* await db.renameTable('oldTableName', 'newTableName');
* });
* ```
*
* Prefix table name with a schema to set a different schema:
*
* ```ts
* import { change } from '../dbScript';
*
* change(async (db) => {
* await db.renameTable('fromSchema.oldTable', 'toSchema.newTable');
* });
* ```
*
* @param from - rename the table from
* @param to - rename the table to
*/
renameTable(from: string, to: string): Promise<void>;
/**
* Set a different schema to the table:
*
* ```ts
* import { change } from '../dbScript';
*
* change(async (db) => {
* await db.changeTableSchema('tableName', 'fromSchema', 'toSchema');
* });
* ```
*
* @param table - table name
* @param from - current table schema
* @param to - desired table schema
*/
changeTableSchema(table: string, from: string, to: string): Promise<void>;
/**
* Add a column to the table on migrating, and remove it on rollback.
*
* `dropColumn` takes the same arguments, removes a column on migrate, and adds it on rollback.
*
* ```ts
* import { change } from '../dbScript';
*
* change(async (db) => {
* await db.addColumn('tableName', 'columnName', (t) =>
* t.integer().index().nullable(),
* );
* });
* ```
*
* @param tableName - name of the table to add the column to
* @param columnName - name of the column to add
* @param fn - function returning a type of the column
*/
addColumn(tableName: string, columnName: string, fn: (t: MigrationColumnTypes<CT>) => ColumnType): Promise<void>;
/**
* Drop the schema, create it on rollback. See {@link addColumn}.
*
* @param tableName - name of the table to add the column to
* @param columnName - name of the column to add
* @param fn - function returning a type of the column
*/
dropColumn(tableName: string, columnName: string, fn: (t: MigrationColumnTypes<CT>) => ColumnType): Promise<void>;
/**
* Add an index to the table on migrating, and remove it on rollback.
*
* `dropIndex` takes the same arguments, removes the index on migrate, and adds it on rollback.
*
* The first argument is the table name, other arguments are the same as in [composite index](#composite-index).
*
* ```ts
* import { change } from '../dbScript';
*
* change(async (db) => {
* await db.addIndex(
* 'tableName',
* ['column1', { column: 'column2', order: 'DESC' }],
* {
* name: 'indexName',
* },
* );
* });
* ```
*
* @param tableName - name of the table to add the index for
* @param columns - indexed columns
* @param args - index options, or an index name and then options
*/
addIndex(tableName: string, columns: (string | TableData.Index.ColumnOrExpressionOptions<string>)[], ...args: [options?: TableData.Index.OptionsArg] | [name?: string, options?: TableData.Index.OptionsArg]): Promise<void>;
/**
* Drop the schema, create it on rollback. See {@link addIndex}.
*
* @param tableName - name of the table to add the index for
* @param columns - indexed columns
* @param args - index options, or an index name and then options
*/
dropIndex(tableName: string, columns: (string | TableData.Index.ColumnOrExpressionOptions<string>)[], ...args: [options?: TableData.Index.OptionsArg] | [name?: string, options?: TableData.Index.OptionsArg]): Promise<void>;
/**
* Rename index:
*
* ```ts
* import { change } from '../dbScript';
*
* change(async (db) => {
* // tableName can be prefixed with a schema
* await db.renameIndex('tableName', 'oldIndexName', 'newIndexName');
* });
* ```
*
* @param tableName - table which this index belongs to
* @param from - rename the index from
* @param to - rename the index to
*/
renameIndex(tableName: string, from: string, to: string): Promise<void>;
/**
* Add a foreign key to a table on migrating, and remove it on rollback.
*
* `dropForeignKey` takes the same arguments, removes the foreign key on migrate, and adds it on rollback.
*
* Arguments:
*
* - table name
* - column names in the table
* - other table name
* - column names in the other table
* - options:
* - `name`: constraint name
* - `match`: 'FULL', 'PARTIAL', or 'SIMPLE'
* - `onUpdate` and `onDelete`: 'NO ACTION', 'RESTRICT', 'CASCADE', 'SET NULL', or 'SET DEFAULT'
*
* The first argument is the table name, other arguments are the same as in [composite foreign key](#composite-foreign-key).
*
* ```ts
* import { change } from '../dbScript';
*
* change(async (db) => {
* await db.addForeignKey(
* 'tableName',
* ['id', 'name'],
* 'otherTable',
* ['foreignId', 'foreignName'],
* {
* name: 'constraintName',
* match: 'FULL',
* onUpdate: 'RESTRICT',
* onDelete: 'CASCADE',
* },
* );
* });
* ```
*
* @param tableName - table name
* @param columns - column names in the table
* @param foreignTable - other table name
* @param foreignColumns - column names in the other table
* @param options - foreign key options
*/
addForeignKey(tableName: string, columns: [string, ...string[]], foreignTable: string, foreignColumns: [string, ...string[]], options?: TableData.References.Options): Promise<void>;
/**
* Drop the schema, create it on rollback. See {@link addForeignKey}.
*
* @param tableName - table name
* @param columns - column names in the table
* @param foreignTable - other table name
* @param foreignColumns - column names in the other table
* @param options - foreign key options
*/
dropForeignKey(tableName: string, columns: [string, ...string[]], foreignTable: string, foreignColumns: [string, ...string[]], options?: TableData.References.Options): Promise<void>;
/**
* Add a primary key to a table on migrate, and remove it on rollback.
*
* `dropPrimaryKey` takes the same arguments, removes the primary key on migrate, and adds it on rollback.
*
* First argument is a table name, second argument is an array of columns.
* The optional third argument may have a name for the primary key constraint.
*
* ```ts
* import { change } from '../dbScript';
*
* change(async (db) => {
* await db.addPrimaryKey('tableName', ['id', 'name'], {
* name: 'tablePkeyName',
* });
* });
* ```
*
* @param tableName - name of the table
* @param columns - array of the columns
* @param name - optionally, set a primary key constraint name
*/
addPrimaryKey(tableName: string, columns: [string, ...string[]], name?: string): Promise<void>;
/**
* Drop the schema, create it on rollback. See {@link addPrimaryKey}.
*
* @param tableName - name of the table
* @param columns - array of the columns
* @param name - optionally, set a primary key constraint name
*/
dropPrimaryKey(tableName: string, columns: [string, ...string[]], name?: string): Promise<void>;
/**
* Add or drop a check for multiple columns.
*
* ```ts
* import { change } from '../dbScript';
*
* change(async (db) => {
* await db.addCheck('tableName', t.sql`column > 123`);
* });
* ```
*
* @param tableName - name of the table to add the check into
* @param check - raw SQL for the check
*/
addCheck(tableName: string, check: RawSQLBase): Promise<void>;
/**
* Drop the schema, create it on rollback. See {@link addCheck}.
*
* @param tableName - name of the table to add the check into
* @param check - raw SQL for the check
*/
dropCheck(tableName: string, check: RawSQLBase): Promise<void>;
/**
* Rename a table constraint such as a primary key or a database check.
*
* ```ts
* import { change } from '../dbScript';
*
* change(async (db) => {
* await db.renameConstraint(
* 'tableName', // may include schema: 'schema.table'
* 'oldConstraintName',
* 'newConstraintName',
* );
* });
* ```
*
* @param tableName - name of the table containing the constraint, may include schema name, may include schema name
* @param from - current name of the constraint
* @param to - desired name
*/
renameConstraint(tableName: string, from: string, to: string): Promise<void>;
/**
* Rename a column:
*
* ```ts
* import { change } from '../dbScript';
*
* change(async (db) => {
* await db.renameColumn('tableName', 'oldColumnName', 'newColumnName');
* });
* ```
*
* @param tableName - name of the table to rename the column in
* @param from - rename column from
* @param to - rename column to
*/
renameColumn(tableName: string, from: string, to: string): Promise<void>;
/**
* `createSchema` creates a database schema, and removes it on rollback.
*
* `dropSchema` takes the same arguments, removes schema on migration, and adds it on rollback.
*
* ```ts
* import { change } from '../dbScript';
*
* change(async (db) => {
* await db.createSchema('schemaName');
* });
* ```
*
* @param schemaName - name of the schema
*/
createSchema(schemaName: string): Promise<void>;
/**
* Renames a database schema, renames it backwards on roll back.
*
* ```ts
* import { change } from '../dbScript';
*
* change(async (db) => {
* await db.renameSchema('from', 'to');
* });
* ```
*
* @param from - existing schema to rename
* @param to - desired schema name
*/
renameSchema(from: string, to: string): Promise<void>;
/**
* Drop the schema, create it on rollback. See {@link createSchema}.
*
* @param schemaName - name of the schema
*/
dropSchema(schemaName: string): Promise<void>;
/**
* `createExtension` creates a database extension, and removes it on rollback.
*
* `dropExtension` takes the same arguments, removes the extension on migrate, and adds it on rollback.
*
* ```ts
* import { change } from '../dbScript';
*
* change(async (db) => {
* await db.createExtension('pg_trgm');
* });
* ```
*
* @param name - name of the extension
* @param options - extension options
*/
createExtension(name: string, options?: RakeDbAst.ExtensionArg): Promise<void>;
/**
* Drop the extension, create it on rollback. See {@link createExtension}.
*
* @param name - name of the extension
* @param options - extension options
*/
dropExtension(name: string, options?: RakeDbAst.ExtensionArg): Promise<void>;
/**
* `createEnum` creates an enum on migrate, drops it on rollback.
*
* `dropEnum` does the opposite.
*
* Third argument for options is optional.
*
* ```ts
* import { change } from '../dbScript';
*
* change(async (db) => {
* await db.createEnum('number', ['one', 'two', 'three']);
*
* // use `schemaName.enumName` format to specify a schema
* await db.createEnum('customSchema.mood', ['sad', 'ok', 'happy'], {
* // following options are used when dropping enum
* dropIfExists: true,
* cascade: true,
* });
* });
* ```
*
* @param name - name of the enum
* @param values - possible enum values
* @param options - enum options
*/
createEnum(name: string, values: [string, ...string[]], options?: Omit<RakeDbAst.Enum, 'type' | 'action' | 'name' | 'values' | 'schema'>): Promise<void>;
/**
* Drop the enum, create it on rollback. See {@link createEnum}.
*
* @param name - name of the enum
* @param values - possible enum values
* @param options - enum options
*/
dropEnum(name: string, values: [string, ...string[]], options?: Omit<RakeDbAst.Enum, 'type' | 'action' | 'name' | 'values' | 'schema'>): Promise<void>;
/**
* Use these methods to add or drop one or multiple values from an existing enum.
*
* `addEnumValues` will drop values when rolling back the migration.
*
* Dropping a value internally acts in multiple steps:
*
* 1. Select all columns from the database that depends on the enum;
* 2. Alter all these columns to have text type;
* 3. Drop the enum;
* 4. Re-create the enum without the value given;
* 5. Alter all columns from the first step to have the enum type;
*
* In the case when the value is used by some table,
* migrating `dropEnumValue` or rolling back `addEnumValue` will throw an error with a descriptive message,
* in such case you'd need to manually resolve the issue by deleting rows with the value, or changing such values.
*
* ```ts
* import { change } from '../dbScript';
*
* change(async (db) => {
* await db.addEnumValue('numbers', 'four');
*
* // you can pass options
* await db.addEnumValue('numbers', 'three', {
* // where to insert
* before: 'four',
* // skip if already exists
* ifNotExists: true,
* });
*
* // enum name can be prefixed with schema
* await db.addEnumValue('public.numbers', 'five', {
* after: 'four',
* });
* });
* ```
*
* @param enumName - target enum name
* @param values - array of values to add
* @param options - optional object with options
* @param options.before - insert before the specified value
* @param options.after - insert after the specified value
* @param options.ifNotExists - skip adding if already exists
*/
addEnumValues(enumName: string, values: string[], options?: AddEnumValueOptions): Promise<void>;
/**
* See {@link addEnumValues}
*/
dropEnumValues(enumName: string, values: string[], options?: AddEnumValueOptions): Promise<void>;
/**
* Rename one or multiple enum values using this method:
*
* ```ts
* import { change } from '../dbScript';
*
* change(async (db) => {
* // rename value "from" to "to"
* await db.rename('numbers', { from: 'to' });
*
* // enum name can be prefixed with schema
* await db.rename('public.numbers', { from: 'to' });
* });
* ```
*
* @param enumName - target enum name, can be prefixed with schema
* @param values - object where keys are for old names, values are for new names
*/
renameEnumValues(enumName: string, values: RecordString): Promise<void>;
/**
* Drops the enum and re-creates it with a new set of values.
* Before dropping, changes all related column types to text, and after creating changes types back to the enum,
* in the same way as [dropEnumValues](/guide/migration-writing.html#addenumvalues,-dropenumvalues) works.
*
* ```ts
* import { change } from '../dbScript';
*
* change(async (db) => {
* await db.changeEnumValues(
* // can be prefixed with schema: 'public.numbers'
* 'numbers',
* // change from:
* ['one', 'two'],
* // change to:
* ['three', 'four'],
* );
* });
* ```
*
* @param enumName - target enum name, can be prefixed with schema
* @param fromValues - array of values before the change
* @param toValues - array of values to set
*/
changeEnumValues(enumName: string, fromValues: string[], toValues: string[]): Promise<void>;
/**
* Rename a type (such as enum):
*
* ```ts
* import { change } from '../dbScript';
*
* change(async (db) => {
* await db.renameType('oldTypeName', 'newTypeName');
* });
* ```
*
* Prefix the type name with a schema to set a different schema:
*
* ```ts
* import { change } from '../dbScript';
*
* change(async (db) => {
* await db.renameType('fromSchema.oldType', 'toSchema.newType');
* });
* ```
*
* @param from - rename the type from
* @param to - rename the type to
*/
renameType(from: string, to: string): Promise<void>;
/**
* Set a different schema to the type (such as enum):
*
* ```ts
* import { change } from '../dbScript';
*
* change(async (db) => {
* await db.changeTypeSchema('typeName', 'fromSchema', 'toSchema');
* });
* ```
*
* @param name - type name
* @param from - current table schema
* @param to - desired table schema
*/
changeTypeSchema(name: string, from: string, to: string): Promise<void>;
/**
* Domain is a custom database type that is based on other type and can include `NOT NULL` and a `CHECK` (see [postgres tutorial](https://www.postgresqltutorial.com/postgresql-tutorial/postgresql-user-defined-data-types/)).
*
* Construct a column type in the function as the second argument.
*
* Specifiers [nullable](/guide/common-column-methods.html#nullable), [default](/guide/common-column-methods.html#default), [check](/guide/migration-column-methods.html#check), [collate](/guide/migration-column-methods.html#collate)
* will be saved to the domain type on database level.
*
* ```ts
* import { change } from '../dbScript';
*
* change(async (db) => {
* await db.createDomain('domainName', (t) =>
* t.integer().check(t.sql`value = 42`),
* );
*
* // use `schemaName.domainName` format to specify a schema
* await db.createDomain('schemaName.domainName', (t) =>
* t
* .text()
* .nullable()
* .collate('C')
* .default('default text')
* .check(t.sql`length(value) > 10`),
* );
* });
* ```
*
* @param name - name of the domain
* @param fn - function returning a column type. Options `nullable`, `collate`, `default`, `check` will be applied to domain
*/
createDomain(name: string, fn: DbDomainArg<CT>): Promise<void>;
/**
* Drop the domain, create it on rollback. See {@link dropDomain}.
*
* @param name - name of the domain
* @param fn - function returning a column type. Options `nullable`, `collate`, `default`, `check` will be applied to domain
*/
dropDomain(name: string, fn: DbDomainArg<CT>): Promise<void>;
/**
* To rename a domain:
*
* ```ts
* import { change } from '../dbScript';
*
* change(async (db) => {
* await db.renameDomain('oldName', 'newName');
*
* // to move domain to a different schema
* await db.renameDomain('oldSchema.domain', 'newSchema.domain');
* });
* ```
*
* @param from - old domain name (can include schema)
* @param to - new domain name (can include schema)
*/
renameDomain(from: string, to: string): Promise<void>;
/**
* Create and drop a database collation, (see [Postgres docs](https://www.postgresql.org/docs/current/sql-createcollation.html)).
*
* ```ts
* import { change } from '../dbScript';
*
* change(async (db) => {
* await db.createCollation('myCollation', {
* // This is a shortcut for setting lcCollate and lcCType at once.
* locale: 'en-u-kn-true',
*
* // set `lcType` and `lcCType` only if the `locale` is not set.
* // lcType: 'C',
* // lcCType: 'C',
*
* // provider can be 'icu' or 'libc'. 'libc' is a default.
* provider: 'icu',
*
* // true by default, false is only supported with 'icu' provider.
* deterministic: true,
*
* // Is intended to by used by `pg_upgrade`. Normally, it should be omitted.
* version: '1.2.3',
*
* // For `CREATE IF NOT EXISTS` when creating.
* createIfNotExists: true,
*
* // For `DROP IF EXISTS` when dropping.
* dropIfExists: true,
*
* // For `DROP ... CASCADE` when dropping.
* cascase: true,
* });
* });
* ```
*
* Instead of specifying the collation options, you can specify a collation to copy options from.
*
* ```ts
* import { change } from '../dbScript';
*
* change(async (db) => {
* await db.createCollation('myCollation', {
* fromExisting: 'otherCollation',
* });
* });
* ```
*
* To create a collation withing a specific database schema, prepend it to the collation name:
*
* ```ts
* import { change } from '../dbScript';
*
* change(async (db) => {
* await db.createCollation('schemaName.myCollation', {
* // `fromExisting` also can accept a collation name with a schema.
* fromExisting: 'schemaName.otherCollation',
* });
* });
* ```
*
* @param name - name of the collation, can contain a name of schema separated with a dot.
* @param options - options to create and drop the collation.
*/
createCollation(name: string, options: Omit<RakeDbAst.Collation, 'type' | 'action' | 'schema' | 'name'>): Promise<void>;
/**
* Drop the collation, create it on rollback. See {@link createCollation}.
*
* @param name - name of the collation, can contain a name of schema separated with a dot.
* @param options - options to create and drop the collation.
*/
dropCollation(name: string, options: Omit<RakeDbAst.Collation, 'type' | 'action' | 'schema' | 'name'>): Promise<void>;
/**
* Create and drop database views.
*
* Provide SQL as a string or via `t.sql` that can accept variables.
*
* ```ts
* import { change } from '../dbScript';
*
* change(async (db) => {
* await db.createView(
* 'simpleView',
* `
* SELECT a.one, b.two
* FROM a
* JOIN b ON b."aId" = a.id
* `,
* );
*
* // view can accept t.sql with variables in such way:
* const value = 'some value';
* await db.createView(
* 'viewWithVariables',
* t.sql`
* SELECT * FROM a WHERE key = ${value}
* `,
* );
*
* // view with options
* await db.createView(
* 'schemaName.recursiveView',
* {
* // createOrReplace has effect when creating the view
* createOrReplace: true,
*
* // dropIfExists and dropMode have effect when dropping the view
* dropIfExists: true,
* dropMode: 'CASCADE',
*
* // for details, check Postgres docs for CREATE VIEW,
* // these options are matching CREATE VIEW options
* temporary: true,
* recursive: true,
* columns: ['n'],
* with: {
* checkOption: 'LOCAL', // or 'CASCADED'
* securityBarrier: true,
* securityInvoker: true,
* },
* },
* `
* VALUES (1)
* UNION ALL
* SELECT n + 1 FROM "schemaName"."recursiveView" WHERE n < 100;
* `,
* );
* });
* ```
*
* @param name - name of the view
* @param options - view options
* @param sql - SQL to create the view with
*/
createView(name: string, options: RakeDbAst.ViewOptions, sql: string | RawSQLBase): Promise<void>;
/**
* See {@link createView}
*
* @param name - name of the view
* @param sql - SQL to create the view with
*/
createView(name: string, sql: string | RawSQLBase): Promise<void>;
/**
* Drop the view, create it on rollback. See {@link createView}.
*
* @param name - name of the view
* @param options - view options
* @param sql - SQL to create the view with
*/
dropView(name: string, options: RakeDbAst.ViewOptions, sql: string | RawSQLBase): Promise<void>;
/**
* Dro