UNPKG

rake-db

Version:
1,359 lines (1,352 loc) 63.3 kB
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