rake-db 2.17.1 → 2.17.2

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/dist/index.d.ts CHANGED
@@ -1,5 +1,5 @@
1
1
  import * as orchid_core from 'orchid-core';
2
- import { EmptyObject, RawSQLBase, ColumnSchemaConfig, MaybeArray, ColumnTypeBase, RecordOptionalString } from 'orchid-core';
2
+ import { EmptyObject, RawSQLBase, ColumnSchemaConfig, MaybeArray, RecordString, ColumnTypeBase, RecordOptionalString } from 'orchid-core';
3
3
  import * as pqb from 'pqb';
4
4
  import { ColumnsShape, Db as Db$1, ColumnType, EnumColumn, raw, Adapter, IndexColumnOptions, IndexOptions, ForeignKeyOptions, DbResult, TransactionAdapter, QueryLogObject, TextColumn, TableData, NoPrimaryKeyOption, SingleColumnIndexOptions, DefaultColumnTypes, DefaultSchemaConfig, QueryLogOptions, AdapterOptions } from 'pqb';
5
5
 
@@ -584,6 +584,116 @@ declare class Migration<CT extends RakeDbColumnTypes> {
584
584
  * @param options - enum options
585
585
  */
586
586
  dropEnum(name: string, values: [string, ...string[]], options?: Omit<RakeDbAst.Enum, 'type' | 'action' | 'name' | 'values' | 'schema'>): Promise<void>;
587
+ /**
588
+ * Use these methods to add or drop one or multiple values from an existing enum.
589
+ *
590
+ * `addEnumValues` will drop values when rolling back the migration.
591
+ *
592
+ * Dropping a value internally acts in multiple steps:
593
+ *
594
+ * 1. Select all columns from the database that depends on the enum;
595
+ * 2. Alter all these columns to have text type;
596
+ * 3. Drop the enum;
597
+ * 4. Re-create the enum without the value given;
598
+ * 5. Alter all columns from the first step to have the enum type;
599
+ *
600
+ * In the case when the value is used by some table,
601
+ * migrating `dropEnumValue` or rolling back `addEnumValue` will throw an error with a descriptive message,
602
+ * in such case you'd need to manually resolve the issue by deleting rows with the value, or changing such values.
603
+ *
604
+ * ```ts
605
+ * import { change } from '../dbScript';
606
+ *
607
+ * change(async (db) => {
608
+ * await db.addEnumValue('numbers', 'four');
609
+ *
610
+ * // you can pass options
611
+ * await db.addEnumValue('numbers', 'three', {
612
+ * // where to insert
613
+ * before: 'four',
614
+ * // skip if already exists
615
+ * ifNotExists: true,
616
+ * });
617
+ *
618
+ * // enum name can be prefixed with schema
619
+ * await db.addEnumValue('public.numbers', 'five', {
620
+ * after: 'four',
621
+ * });
622
+ * });
623
+ * ```
624
+ *
625
+ * @param enumName - target enum name
626
+ * @param values - array of values to add
627
+ * @param options - optional object with options
628
+ * @param options.before - insert before the specified value
629
+ * @param options.after - insert after the specified value
630
+ * @param options.ifNotExists - skip adding if already exists
631
+ */
632
+ addEnumValues(enumName: string, values: string[], options?: AddEnumValueOptions): Promise<void>;
633
+ /**
634
+ * See {@link addEnumValues}
635
+ */
636
+ dropEnumValues(enumName: string, values: string[], options?: AddEnumValueOptions): Promise<void>;
637
+ /**
638
+ * Rename one or multiple enum values using this method:
639
+ *
640
+ * ```ts
641
+ * import { change } from '../dbScript';
642
+ *
643
+ * change(async (db) => {
644
+ * // rename value "from" to "to"
645
+ * await db.rename('numbers', { from: 'to' });
646
+ *
647
+ * // enum name can be prefixed with schema
648
+ * await db.rename('public.numbers', { from: 'to' });
649
+ * });
650
+ * ```
651
+ *
652
+ * @param enumName - target enum name, can be prefixed with schema
653
+ * @param values - object where keys are for old names, values are for new names
654
+ */
655
+ renameEnumValues(enumName: string, values: RecordString): Promise<void>;
656
+ /**
657
+ * Rename a type (such as enum):
658
+ *
659
+ * ```ts
660
+ * import { change } from '../dbScript';
661
+ *
662
+ * change(async (db) => {
663
+ * await db.renameType('oldTypeName', 'newTypeName');
664
+ * });
665
+ * ```
666
+ *
667
+ * Prefix the type name with a schema to set a different schema:
668
+ *
669
+ * ```ts
670
+ * import { change } from '../dbScript';
671
+ *
672
+ * change(async (db) => {
673
+ * await db.renameType('fromSchema.oldType', 'toSchema.newType');
674
+ * });
675
+ * ```
676
+ *
677
+ * @param from - rename the type from
678
+ * @param to - rename the type to
679
+ */
680
+ renameType(from: string, to: string): Promise<void>;
681
+ /**
682
+ * Set a different schema to the type (such as enum):
683
+ *
684
+ * ```ts
685
+ * import { change } from '../dbScript';
686
+ *
687
+ * change(async (db) => {
688
+ * await db.changeTypeSchema('typeName', 'fromSchema', 'toSchema');
689
+ * });
690
+ * ```
691
+ *
692
+ * @param name - type name
693
+ * @param from - current table schema
694
+ * @param to - desired table schema
695
+ */
696
+ changeTypeSchema(name: string, from: string, to: string): Promise<void>;
587
697
  /**
588
698
  * Domain is a custom database type that allows to predefine a `NOT NULL` and a `CHECK` (see [postgres tutorial](https://www.postgresqltutorial.com/postgresql-tutorial/postgresql-user-defined-data-types/)).
589
699
  *
@@ -833,8 +943,15 @@ declare class Migration<CT extends RakeDbColumnTypes> {
833
943
  */
834
944
  constraintExists(constraintName: string): Promise<boolean>;
835
945
  }
946
+ declare const renameType: (migration: Migration<RakeDbColumnTypes>, from: string, to: string, table: boolean) => Promise<void>;
947
+ interface AddEnumValueOptions {
948
+ ifNotExists?: boolean;
949
+ before?: string;
950
+ after?: string;
951
+ }
952
+ declare const addOrDropEnumValues: (migration: Migration<RakeDbColumnTypes>, up: boolean, enumName: string, values: string[], options?: AddEnumValueOptions) => Promise<void>;
836
953
 
837
- type RakeDbAst = RakeDbAst.Table | RakeDbAst.ChangeTable | RakeDbAst.RenameTable | RakeDbAst.Schema | RakeDbAst.Extension | RakeDbAst.Enum | RakeDbAst.Domain | RakeDbAst.Collation | RakeDbAst.Constraint | RakeDbAst.View;
954
+ type RakeDbAst = RakeDbAst.Table | RakeDbAst.ChangeTable | RakeDbAst.RenameType | RakeDbAst.Schema | RakeDbAst.Extension | RakeDbAst.Enum | RakeDbAst.EnumValues | RakeDbAst.RenameEnumValues | RakeDbAst.Domain | RakeDbAst.Collation | RakeDbAst.Constraint | RakeDbAst.View;
838
955
  declare namespace RakeDbAst {
839
956
  interface Table extends TableData {
840
957
  type: 'table';
@@ -893,8 +1010,9 @@ declare namespace RakeDbAst {
893
1010
  indexes?: Omit<SingleColumnIndexOptions, 'column' | 'expression'>[];
894
1011
  identity?: TableData.Identity;
895
1012
  }
896
- interface RenameTable {
897
- type: 'renameTable';
1013
+ interface RenameType {
1014
+ type: 'renameType';
1015
+ table: boolean;
898
1016
  fromSchema?: string;
899
1017
  from: string;
900
1018
  toSchema?: string;
@@ -924,6 +1042,22 @@ declare namespace RakeDbAst {
924
1042
  cascade?: boolean;
925
1043
  dropIfExists?: boolean;
926
1044
  }
1045
+ interface EnumValues {
1046
+ type: 'enumValues';
1047
+ action: 'add' | 'drop';
1048
+ schema?: string;
1049
+ name: string;
1050
+ values: string[];
1051
+ place?: 'before' | 'after';
1052
+ relativeTo?: string;
1053
+ ifNotExists?: boolean;
1054
+ }
1055
+ interface RenameEnumValues {
1056
+ type: 'renameEnumValues';
1057
+ schema?: string;
1058
+ name: string;
1059
+ values: RecordString;
1060
+ }
927
1061
  interface Domain {
928
1062
  type: 'domain';
929
1063
  action: 'create' | 'drop';
@@ -1268,4 +1402,4 @@ type RakeDbChangeFn<CT extends RakeDbColumnTypes> = (fn: ChangeCallback<CT>) =>
1268
1402
  declare const rakeDb: RakeDbFn;
1269
1403
  declare const rakeDbAliases: RecordOptionalString;
1270
1404
 
1271
- export { AnyRakeDbConfig, AppCodeUpdater, AppCodeUpdaterParams, ChangeTableCallback, ChangeTableOptions, ColumnComment, ColumnsShapeCallback, ConstraintArg, DbMigration, DropMode, InputRakeDbConfig, Migration, MigrationAdapter, MigrationColumnTypes, ModuleExportsRecord, NoMigrationsTableError, RAKE_DB_LOCK_KEY, RakeDbAppliedVersions, RakeDbAst, RakeDbBaseTable, RakeDbChangeFn, RakeDbColumnTypes, RakeDbConfig, RakeDbFn, RakeDbFnReturns, RakeDbLazyFn, RakeDbMigrationId, RakeDbResult, SilentQueries, TableOptions, changeCache, createDb, createMigrationInterface, deleteMigratedVersion, dropDb, generate, generateTimeStamp, getDatabaseAndUserFromOptions, getMigratedVersionsMap, makeFileVersion, migrate, migrateOrRollback, migrationConfigDefaults, processRakeDbConfig, rakeDb, rakeDbAliases, redo, resetDb, rollback, saveMigratedVersion, writeMigrationFile };
1405
+ export { AnyRakeDbConfig, AppCodeUpdater, AppCodeUpdaterParams, ChangeTableCallback, ChangeTableOptions, ColumnComment, ColumnsShapeCallback, ConstraintArg, DbMigration, DropMode, InputRakeDbConfig, Migration, MigrationAdapter, MigrationColumnTypes, ModuleExportsRecord, NoMigrationsTableError, RAKE_DB_LOCK_KEY, RakeDbAppliedVersions, RakeDbAst, RakeDbBaseTable, RakeDbChangeFn, RakeDbColumnTypes, RakeDbConfig, RakeDbFn, RakeDbFnReturns, RakeDbLazyFn, RakeDbMigrationId, RakeDbResult, SilentQueries, TableOptions, addOrDropEnumValues, changeCache, createDb, createMigrationInterface, deleteMigratedVersion, dropDb, generate, generateTimeStamp, getDatabaseAndUserFromOptions, getMigratedVersionsMap, makeFileVersion, migrate, migrateOrRollback, migrationConfigDefaults, processRakeDbConfig, rakeDb, rakeDbAliases, redo, renameType, resetDb, rollback, saveMigratedVersion, writeMigrationFile };
package/dist/index.js CHANGED
@@ -1218,28 +1218,8 @@ class Migration {
1218
1218
  * @param from - rename the table from
1219
1219
  * @param to - rename the table to
1220
1220
  */
1221
- async renameTable(from, to) {
1222
- var _a;
1223
- const [fromSchema, f] = getSchemaAndTableFromName(this.up ? from : to);
1224
- const [toSchema, t] = getSchemaAndTableFromName(this.up ? to : from);
1225
- const ast = {
1226
- type: "renameTable",
1227
- fromSchema,
1228
- from: f,
1229
- toSchema,
1230
- to: t
1231
- };
1232
- if (ast.from !== ast.to) {
1233
- await this.adapter.query(
1234
- `ALTER TABLE ${quoteTable(ast.fromSchema, ast.from)} RENAME TO "${ast.to}"`
1235
- );
1236
- }
1237
- if (ast.fromSchema !== ast.toSchema) {
1238
- await this.adapter.query(
1239
- `ALTER TABLE ${quoteTable(ast.fromSchema, ast.to)} SET SCHEMA "${(_a = ast.toSchema) != null ? _a : this.adapter.schema}"`
1240
- );
1241
- }
1242
- this.migratedAsts.push(ast);
1221
+ renameTable(from, to) {
1222
+ return renameType(this, from, to, true);
1243
1223
  }
1244
1224
  /**
1245
1225
  * Set a different schema to the table:
@@ -1609,6 +1589,141 @@ class Migration {
1609
1589
  dropEnum(name, values, options) {
1610
1590
  return createEnum$1(this, !this.up, name, values, options);
1611
1591
  }
1592
+ /**
1593
+ * Use these methods to add or drop one or multiple values from an existing enum.
1594
+ *
1595
+ * `addEnumValues` will drop values when rolling back the migration.
1596
+ *
1597
+ * Dropping a value internally acts in multiple steps:
1598
+ *
1599
+ * 1. Select all columns from the database that depends on the enum;
1600
+ * 2. Alter all these columns to have text type;
1601
+ * 3. Drop the enum;
1602
+ * 4. Re-create the enum without the value given;
1603
+ * 5. Alter all columns from the first step to have the enum type;
1604
+ *
1605
+ * In the case when the value is used by some table,
1606
+ * migrating `dropEnumValue` or rolling back `addEnumValue` will throw an error with a descriptive message,
1607
+ * in such case you'd need to manually resolve the issue by deleting rows with the value, or changing such values.
1608
+ *
1609
+ * ```ts
1610
+ * import { change } from '../dbScript';
1611
+ *
1612
+ * change(async (db) => {
1613
+ * await db.addEnumValue('numbers', 'four');
1614
+ *
1615
+ * // you can pass options
1616
+ * await db.addEnumValue('numbers', 'three', {
1617
+ * // where to insert
1618
+ * before: 'four',
1619
+ * // skip if already exists
1620
+ * ifNotExists: true,
1621
+ * });
1622
+ *
1623
+ * // enum name can be prefixed with schema
1624
+ * await db.addEnumValue('public.numbers', 'five', {
1625
+ * after: 'four',
1626
+ * });
1627
+ * });
1628
+ * ```
1629
+ *
1630
+ * @param enumName - target enum name
1631
+ * @param values - array of values to add
1632
+ * @param options - optional object with options
1633
+ * @param options.before - insert before the specified value
1634
+ * @param options.after - insert after the specified value
1635
+ * @param options.ifNotExists - skip adding if already exists
1636
+ */
1637
+ addEnumValues(enumName, values, options) {
1638
+ return addOrDropEnumValues(this, this.up, enumName, values, options);
1639
+ }
1640
+ /**
1641
+ * See {@link addEnumValues}
1642
+ */
1643
+ dropEnumValues(enumName, values, options) {
1644
+ return addOrDropEnumValues(this, !this.up, enumName, values, options);
1645
+ }
1646
+ /**
1647
+ * Rename one or multiple enum values using this method:
1648
+ *
1649
+ * ```ts
1650
+ * import { change } from '../dbScript';
1651
+ *
1652
+ * change(async (db) => {
1653
+ * // rename value "from" to "to"
1654
+ * await db.rename('numbers', { from: 'to' });
1655
+ *
1656
+ * // enum name can be prefixed with schema
1657
+ * await db.rename('public.numbers', { from: 'to' });
1658
+ * });
1659
+ * ```
1660
+ *
1661
+ * @param enumName - target enum name, can be prefixed with schema
1662
+ * @param values - object where keys are for old names, values are for new names
1663
+ */
1664
+ async renameEnumValues(enumName, values) {
1665
+ const [schema, name] = getSchemaAndTableFromName(enumName);
1666
+ const ast = {
1667
+ type: "renameEnumValues",
1668
+ schema,
1669
+ name,
1670
+ values
1671
+ };
1672
+ for (const pair of Object.entries(ast.values)) {
1673
+ const [from, to] = this.up ? pair : [pair[1], pair[0]];
1674
+ await this.adapter.query(
1675
+ `ALTER TYPE ${quoteTable(
1676
+ ast.schema,
1677
+ ast.name
1678
+ )} RENAME VALUE "${from}" TO "${to}"`
1679
+ );
1680
+ }
1681
+ }
1682
+ /**
1683
+ * Rename a type (such as enum):
1684
+ *
1685
+ * ```ts
1686
+ * import { change } from '../dbScript';
1687
+ *
1688
+ * change(async (db) => {
1689
+ * await db.renameType('oldTypeName', 'newTypeName');
1690
+ * });
1691
+ * ```
1692
+ *
1693
+ * Prefix the type name with a schema to set a different schema:
1694
+ *
1695
+ * ```ts
1696
+ * import { change } from '../dbScript';
1697
+ *
1698
+ * change(async (db) => {
1699
+ * await db.renameType('fromSchema.oldType', 'toSchema.newType');
1700
+ * });
1701
+ * ```
1702
+ *
1703
+ * @param from - rename the type from
1704
+ * @param to - rename the type to
1705
+ */
1706
+ renameType(from, to) {
1707
+ return renameType(this, from, to, false);
1708
+ }
1709
+ /**
1710
+ * Set a different schema to the type (such as enum):
1711
+ *
1712
+ * ```ts
1713
+ * import { change } from '../dbScript';
1714
+ *
1715
+ * change(async (db) => {
1716
+ * await db.changeTypeSchema('typeName', 'fromSchema', 'toSchema');
1717
+ * });
1718
+ * ```
1719
+ *
1720
+ * @param name - type name
1721
+ * @param from - current table schema
1722
+ * @param to - desired table schema
1723
+ */
1724
+ changeTypeSchema(name, from, to) {
1725
+ return this.renameType(`${from}.${name}`, `${to}.${name}`);
1726
+ }
1612
1727
  /**
1613
1728
  * Domain is a custom database type that allows to predefine a `NOT NULL` and a `CHECK` (see [postgres tutorial](https://www.postgresqltutorial.com/postgresql-tutorial/postgresql-user-defined-data-types/)).
1614
1729
  *
@@ -1968,6 +2083,104 @@ const createCollation$1 = async (migration, up, name, options) => {
1968
2083
  const queryExists = (db, sql) => {
1969
2084
  return db.adapter.query(sql).then(({ rowCount }) => rowCount > 0);
1970
2085
  };
2086
+ const renameType = async (migration, from, to, table) => {
2087
+ var _a;
2088
+ const [fromSchema, f] = getSchemaAndTableFromName(migration.up ? from : to);
2089
+ const [toSchema, t] = getSchemaAndTableFromName(migration.up ? to : from);
2090
+ const ast = {
2091
+ type: "renameType",
2092
+ table,
2093
+ fromSchema,
2094
+ from: f,
2095
+ toSchema,
2096
+ to: t
2097
+ };
2098
+ const sqlKind = ast.table ? "TABLE" : "TYPE";
2099
+ if (ast.from !== ast.to) {
2100
+ await migration.adapter.query(
2101
+ `ALTER ${sqlKind} ${quoteTable(ast.fromSchema, ast.from)} RENAME TO "${ast.to}"`
2102
+ );
2103
+ }
2104
+ if (ast.fromSchema !== ast.toSchema) {
2105
+ await migration.adapter.query(
2106
+ `ALTER ${sqlKind} ${quoteTable(ast.fromSchema, ast.to)} SET SCHEMA "${(_a = ast.toSchema) != null ? _a : migration.adapter.schema}"`
2107
+ );
2108
+ }
2109
+ migration.migratedAsts.push(ast);
2110
+ };
2111
+ const addOrDropEnumValues = async (migration, up, enumName, values, options) => {
2112
+ var _a;
2113
+ const defaultSchema = migration.adapter.schema;
2114
+ const [schema, name] = getSchemaAndTableFromName(enumName);
2115
+ const quotedName = quoteTable(schema, name);
2116
+ const ast = {
2117
+ type: "enumValues",
2118
+ action: up ? "add" : "drop",
2119
+ schema,
2120
+ name,
2121
+ values,
2122
+ place: (options == null ? void 0 : options.before) ? "before" : (options == null ? void 0 : options.after) ? "after" : void 0,
2123
+ relativeTo: (_a = options == null ? void 0 : options.before) != null ? _a : options == null ? void 0 : options.after,
2124
+ ifNotExists: options == null ? void 0 : options.ifNotExists
2125
+ };
2126
+ if (ast.action === "add") {
2127
+ await Promise.all(
2128
+ (ast.place === "after" ? [...ast.values].reverse() : ast.values).map(
2129
+ (value) => migration.adapter.query(
2130
+ `ALTER TYPE ${quoteTable(ast.schema, ast.name)} ADD VALUE${ast.ifNotExists ? " IF NOT EXISTS" : ""} ${orchidCore.singleQuote(value)}${ast.place && ast.relativeTo ? ` ${ast.place.toUpperCase()} ${orchidCore.singleQuote(ast.relativeTo)}` : ""}`
2131
+ )
2132
+ )
2133
+ );
2134
+ return;
2135
+ }
2136
+ const { rows: valuesRows } = await migration.adapter.query(
2137
+ `SELECT unnest(enum_range(NULL::${quotedName}))::text value`
2138
+ );
2139
+ const existingValues = valuesRows.map((r) => r.value);
2140
+ const { rows: tables } = await migration.adapter.query(
2141
+ `SELECT n.nspname AS "schema",
2142
+ c.relname AS "table",
2143
+ json_agg(a.attname ORDER BY a.attnum) AS "columns"
2144
+ FROM pg_class c
2145
+ JOIN pg_catalog.pg_namespace n ON n.oid = relnamespace
2146
+ JOIN pg_attribute a ON a.attrelid = c.oid
2147
+ JOIN pg_type t ON a.atttypid = t.oid AND t.typname = ${orchidCore.singleQuote(name)}
2148
+ JOIN pg_namespace tn ON tn.oid = t.typnamespace AND tn.nspname = ${orchidCore.singleQuote(
2149
+ schema != null ? schema : defaultSchema
2150
+ )}
2151
+ GROUP BY n.nspname, c.relname`
2152
+ );
2153
+ const sql = tables.map(
2154
+ (t) => `ALTER TABLE ${quoteTable(t.schema, t.table)}
2155
+ ${t.columns.map((c) => ` ALTER COLUMN "${c}" TYPE text`).join(",\n")}`
2156
+ );
2157
+ sql.push(
2158
+ `DROP TYPE ${quotedName}`,
2159
+ `CREATE TYPE ${quotedName} AS ENUM (${existingValues.filter((v) => !ast.values.includes(v)).map(orchidCore.singleQuote).join(", ")})`
2160
+ );
2161
+ await migration.adapter.query(sql.join(";\n"));
2162
+ for (const t of tables) {
2163
+ const table = quoteTable(t.schema, t.table);
2164
+ for (const c of t.columns) {
2165
+ try {
2166
+ await migration.adapter.query(
2167
+ `ALTER TABLE ${table}
2168
+ ALTER COLUMN "${c}" TYPE ${quotedName} USING "${c}"::${quotedName}`
2169
+ );
2170
+ } catch (err) {
2171
+ if (err.code === "22P02") {
2172
+ throw new Error(
2173
+ `Cannot drop ${quotedName} enum values [${ast.values.map(orchidCore.singleQuote).join(
2174
+ ", "
2175
+ )}]: table ${table} has a row with such value in the column "${c}"`,
2176
+ { cause: err }
2177
+ );
2178
+ }
2179
+ throw err;
2180
+ }
2181
+ }
2182
+ }
2183
+ };
1971
2184
 
1972
2185
  const writeMigrationFile = async (config, version, name, content) => {
1973
2186
  var _a;
@@ -4598,6 +4811,7 @@ Migrate and rollback common arguments:
4598
4811
  exports.Migration = Migration;
4599
4812
  exports.NoMigrationsTableError = NoMigrationsTableError;
4600
4813
  exports.RAKE_DB_LOCK_KEY = RAKE_DB_LOCK_KEY;
4814
+ exports.addOrDropEnumValues = addOrDropEnumValues;
4601
4815
  exports.changeCache = changeCache;
4602
4816
  exports.createDb = createDb;
4603
4817
  exports.createMigrationInterface = createMigrationInterface;
@@ -4615,6 +4829,7 @@ exports.processRakeDbConfig = processRakeDbConfig;
4615
4829
  exports.rakeDb = rakeDb;
4616
4830
  exports.rakeDbAliases = rakeDbAliases;
4617
4831
  exports.redo = redo;
4832
+ exports.renameType = renameType;
4618
4833
  exports.resetDb = resetDb;
4619
4834
  exports.rollback = rollback;
4620
4835
  exports.saveMigratedVersion = saveMigratedVersion;