rake-db 2.30.0 → 2.30.1

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.mjs CHANGED
@@ -179,10 +179,6 @@ const promptText = ({
179
179
  };
180
180
 
181
181
  const getMaybeTransactionAdapter = (db) => "$getAdapter" in db ? db.$getAdapter() : db;
182
- const ensureTransaction = (db, fn) => {
183
- const adapter = getMaybeTransactionAdapter(db);
184
- return adapter.isInTransaction() ? fn(adapter) : adapter.transaction(fn);
185
- };
186
182
  const runSqlInSavePoint = async (db, sql, code) => {
187
183
  const adapter = getMaybeTransactionAdapter(db);
188
184
  try {
@@ -313,10 +309,12 @@ const makePopulateEnumQuery = (schema, item) => {
313
309
  const transaction = (adapter, config, fn) => {
314
310
  const searchPath = config.transactionSearchPath;
315
311
  return adapter.transaction(
316
- fn,
317
312
  searchPath ? {
318
- searchPath: typeof searchPath === "function" ? searchPath() : searchPath
319
- } : void 0
313
+ locals: {
314
+ search_path: typeof searchPath === "function" ? searchPath() : searchPath
315
+ }
316
+ } : void 0,
317
+ fn
320
318
  );
321
319
  };
322
320
  const queryLock = (trx) => trx.query(`SELECT pg_advisory_xact_lock('${RAKE_DB_LOCK_KEY}')`);
@@ -790,7 +788,7 @@ const createTable = async (migration, up, tableName, first, second, third) => {
790
788
  shape = tableData = emptyObject;
791
789
  }
792
790
  const schema = migration.adapter.getSchema();
793
- const ast = makeAst$2(
791
+ const ast = makeAst$3(
794
792
  schema,
795
793
  up,
796
794
  tableName,
@@ -820,7 +818,7 @@ const createTable = async (migration, up, tableName, first, second, third) => {
820
818
  }
821
819
  };
822
820
  };
823
- const makeAst$2 = (schema, up, tableName, shape, tableData, options, noPrimaryKey) => {
821
+ const makeAst$3 = (schema, up, tableName, shape, tableData, options, noPrimaryKey) => {
824
822
  const shapePKeys = [];
825
823
  for (const key in shape) {
826
824
  const column = shape[key];
@@ -1170,7 +1168,7 @@ const changeTable = async (migration, up, tableName, options, fn) => {
1170
1168
  addOrDropChanges.length = 0;
1171
1169
  const changeData = fn?.(tableChanger) || {};
1172
1170
  const schema = migration.adapter.getSchema();
1173
- const ast = makeAst$1(
1171
+ const ast = makeAst$2(
1174
1172
  schema,
1175
1173
  up,
1176
1174
  tableName,
@@ -1184,7 +1182,7 @@ const changeTable = async (migration, up, tableName, options, fn) => {
1184
1182
  query.then?.(result);
1185
1183
  }
1186
1184
  };
1187
- const makeAst$1 = (schema, up, name, changeData, changeTableData2, options) => {
1185
+ const makeAst$2 = (schema, up, name, changeData, changeTableData2, options) => {
1188
1186
  const { comment } = options;
1189
1187
  const shape = {};
1190
1188
  const consumedChanges = {};
@@ -1636,11 +1634,11 @@ const renameColumnSql = (from, to) => {
1636
1634
 
1637
1635
  const createView = async (migration, up, name, options, sql) => {
1638
1636
  const schema = migration.adapter.getSchema();
1639
- const ast = makeAst(schema, up, name, options, sql);
1640
- const query = astToQuery(ast);
1637
+ const ast = makeAst$1(schema, up, name, options, sql);
1638
+ const query = astToQuery$1(ast);
1641
1639
  await migration.adapter.arrays(interpolateSqlValues(query));
1642
1640
  };
1643
- const makeAst = (schema, up, fullName, options, sql) => {
1641
+ const makeAst$1 = (schema, up, fullName, options, sql) => {
1644
1642
  if (typeof sql === "string") {
1645
1643
  sql = raw({ raw: sql });
1646
1644
  }
@@ -1656,7 +1654,7 @@ const makeAst = (schema, up, fullName, options, sql) => {
1656
1654
  deps: []
1657
1655
  };
1658
1656
  };
1659
- const astToQuery = (ast) => {
1657
+ const astToQuery$1 = (ast) => {
1660
1658
  const values = [];
1661
1659
  const sql = [];
1662
1660
  const { options } = ast;
@@ -1693,6 +1691,130 @@ const astToQuery = (ast) => {
1693
1691
  };
1694
1692
  };
1695
1693
 
1694
+ const serializers = {
1695
+ super: (b) => `${b ? "" : "NO"}SUPERUSER`,
1696
+ inherit: (b) => `${b ? "" : "NO"}INHERIT`,
1697
+ createRole: (b) => `${b ? "" : "NO"}CREATEROLE`,
1698
+ createDb: (b) => `${b ? "" : "NO"}CREATEDB`,
1699
+ canLogin: (b) => `${b ? "" : "NO"}LOGIN`,
1700
+ replication: (b) => `${b ? "" : "NO"}REPLICATION`,
1701
+ bypassRls: (b) => `${b ? "" : "NO"}BYPASSRLS`,
1702
+ connLimit: (value) => `CONNECTION LIMIT ${value === void 0 ? -1 : value}`,
1703
+ validUntil: (value) => `VALID UNTIL '${value === void 0 ? "infinity" : value}'`
1704
+ };
1705
+ const createOrDropRole = async (migration, up, name, params) => {
1706
+ const ast = makeAst(up, name, params);
1707
+ const sql = astToQuery(ast);
1708
+ await migration.adapter.arrays(sql);
1709
+ };
1710
+ const makeAst = (up, name, params) => {
1711
+ return {
1712
+ type: "role",
1713
+ action: up ? "create" : "drop",
1714
+ name,
1715
+ super: false,
1716
+ inherit: false,
1717
+ createRole: false,
1718
+ createDb: false,
1719
+ canLogin: false,
1720
+ replication: false,
1721
+ connLimit: -1,
1722
+ bypassRls: false,
1723
+ ...params
1724
+ };
1725
+ };
1726
+ const astToQuery = (ast) => {
1727
+ const w = [];
1728
+ if (ast.action !== "drop") {
1729
+ for (const key in ast) {
1730
+ if (key in serializers && (key !== "connLimit" || ast[key] !== -1)) {
1731
+ let value = ast[key];
1732
+ if (value instanceof Date) value = value.toISOString();
1733
+ w.push(serializers[key](value));
1734
+ }
1735
+ }
1736
+ }
1737
+ let sql = `${ast.action.toUpperCase()} ROLE "${ast.name}"${w.length ? ` WITH ${w.join(" ")}` : ""}`;
1738
+ if (ast.action !== "drop" && ast.config) {
1739
+ for (const [key, value] of Object.entries(ast.config)) {
1740
+ sql += `;
1741
+ ALTER ROLE "${ast.name}" SET ${key} = '${value}'`;
1742
+ }
1743
+ }
1744
+ return sql;
1745
+ };
1746
+ const changeRole = async (migration, up, name, from, to) => {
1747
+ if (!up) {
1748
+ if (to.name) {
1749
+ from = { ...from, name };
1750
+ name = to.name;
1751
+ }
1752
+ const f = from;
1753
+ from = to;
1754
+ to = { ...f };
1755
+ for (const key in from) {
1756
+ if (!(key in to)) {
1757
+ to[key] = void 0;
1758
+ }
1759
+ }
1760
+ if (from.config) {
1761
+ const config = to.config ?? (to.config = {});
1762
+ for (const key in from.config) {
1763
+ if (!(key in config)) {
1764
+ config[key] = void 0;
1765
+ }
1766
+ }
1767
+ }
1768
+ }
1769
+ const ast = makeChangeAst(name, from, to);
1770
+ const sql = changeAstToQuery(ast);
1771
+ if (sql) await migration.adapter.arrays(sql);
1772
+ };
1773
+ const makeChangeAst = (name, from, to) => {
1774
+ return {
1775
+ type: "changeRole",
1776
+ name,
1777
+ from,
1778
+ to
1779
+ };
1780
+ };
1781
+ const changeAstToQuery = ({ name, from, to }) => {
1782
+ const queries = [];
1783
+ if (to.name && to.name !== name) {
1784
+ queries.push(`ALTER ROLE "${name}" RENAME TO "${to.name}"`);
1785
+ name = to.name;
1786
+ }
1787
+ const w = [];
1788
+ for (const key in to) {
1789
+ let value = to[key];
1790
+ if (key !== "config") {
1791
+ if (value instanceof Date) value = value.toISOString();
1792
+ let other = from[key];
1793
+ if (other instanceof Date) other = other.toISOString();
1794
+ if (value !== other && key in serializers) {
1795
+ w.push(serializers[key](value));
1796
+ }
1797
+ }
1798
+ }
1799
+ if (w.length) {
1800
+ queries.push(`ALTER ROLE "${name}" WITH ${w.join(" ")}`);
1801
+ }
1802
+ const config = to.config;
1803
+ if (config) {
1804
+ const fromConfig = from.config ?? emptyObject;
1805
+ for (const key in config) {
1806
+ const value = config[key];
1807
+ const other = fromConfig[key];
1808
+ if (value !== other) {
1809
+ queries.push(
1810
+ `ALTER ROLE "${name}" ${value ? `SET ${key} = '${value}'` : `RESET ${key}`}`
1811
+ );
1812
+ }
1813
+ }
1814
+ }
1815
+ return queries.join(";\n");
1816
+ };
1817
+
1696
1818
  const createMigrationInterface = (tx, up, config) => {
1697
1819
  const adapter = Object.create(tx);
1698
1820
  const { query, arrays } = adapter;
@@ -2576,6 +2698,21 @@ class Migration {
2576
2698
  values: [constraintName]
2577
2699
  });
2578
2700
  }
2701
+ createRole(name, params) {
2702
+ return createOrDropRole(this, this.up, name, params);
2703
+ }
2704
+ dropRole(name, params) {
2705
+ return createOrDropRole(this, !this.up, name, params);
2706
+ }
2707
+ changeRole(name, params) {
2708
+ return changeRole(
2709
+ this,
2710
+ this.up,
2711
+ name,
2712
+ params.from || emptyObject,
2713
+ params.to
2714
+ );
2715
+ }
2579
2716
  }
2580
2717
  const wrapWithLog = async (log, text, values, fn) => {
2581
2718
  if (!log) {
@@ -3379,7 +3516,7 @@ async function renameMigrations(config, trx, versions, renameTo) {
3379
3516
  }
3380
3517
 
3381
3518
  const transactionIfSingle = (adapter, config, fn) => {
3382
- return !adapter.isInTransaction() && config.transaction === "single" ? transaction(adapter, config, fn) : fn(adapter);
3519
+ return config.transaction === "single" ? transaction(adapter, config, fn) : fn(adapter);
3383
3520
  };
3384
3521
  function makeMigrateFn(up, defaultCount, fn) {
3385
3522
  return async (db, config, params) => {
@@ -3432,14 +3569,16 @@ const migrateAndClose = async (db, config, params) => {
3432
3569
  await migrate(adapter, config, params);
3433
3570
  await adapter.close();
3434
3571
  };
3435
- const runMigration = async (db, migration) => {
3436
- await ensureTransaction(db, async (trx) => {
3572
+ async function runMigration(db, ...args) {
3573
+ const [config, migration] = args.length === 1 ? [{}, args[0]] : [args[0], args[1]];
3574
+ const adapter = getMaybeTransactionAdapter(db);
3575
+ await transaction(adapter, config, async (trx) => {
3437
3576
  clearChanges();
3438
3577
  const changes = await getChanges({ load: migration });
3439
- const config = changes[0]?.config;
3440
- await applyMigration(trx, true, changes, config);
3578
+ const config2 = changes[0]?.config;
3579
+ await applyMigration(trx, true, changes, config2);
3441
3580
  });
3442
- };
3581
+ }
3443
3582
  const rollback = makeMigrateFn(
3444
3583
  false,
3445
3584
  1,
@@ -3503,7 +3642,7 @@ const migrateOrRollback = async (trx, config, set, versions, count, up, redo2, f
3503
3642
  }
3504
3643
  let loggedAboutStarting = false;
3505
3644
  let migrations;
3506
- const migrationRunner = trx.isInTransaction() || config.transaction === "single" ? applyMigration : runMigrationInOwnTransaction;
3645
+ const migrationRunner = config.transaction === "single" ? applyMigration : runMigrationInOwnTransaction;
3507
3646
  for (const file of set.migrations) {
3508
3647
  if (up && versionsMap[file.version] || !up && !versionsMap[file.version]) {
3509
3648
  continue;
@@ -3944,6 +4083,10 @@ const astToGenerateItem = (config, ast, currentSchema) => {
3944
4083
  deps.push(tableSchema, `${tableSchema}.${tableName}`);
3945
4084
  break;
3946
4085
  }
4086
+ case "role":
4087
+ case "changeRole": {
4088
+ break;
4089
+ }
3947
4090
  default:
3948
4091
  exhaustive(ast);
3949
4092
  }
@@ -4524,7 +4667,60 @@ const astEncoders = {
4524
4667
  }
4525
4668
  addCode(code, ");");
4526
4669
  return code;
4670
+ },
4671
+ role(ast) {
4672
+ const params = ast.action === "create" ? roleParams(ast) : void 0;
4673
+ const arr = [
4674
+ `await db.${ast.action}Role(${singleQuote(ast.name)}${params?.length ? ", {" : ");"}`
4675
+ ];
4676
+ if (params?.length) {
4677
+ arr.push(params);
4678
+ arr.push("});");
4679
+ }
4680
+ return arr;
4681
+ },
4682
+ changeRole(ast) {
4683
+ const from = roleParams(ast.from, ast.to);
4684
+ const to = roleParams(ast.to, ast.from, true);
4685
+ return [
4686
+ `await db.changeRole(${singleQuote(ast.name)}, {`,
4687
+ [...from.length ? ["from: {", from, "},"] : [], "to: {", to, "},"],
4688
+ "});"
4689
+ ];
4690
+ }
4691
+ };
4692
+ const roleParams = (ast, compare, to) => {
4693
+ const params = [];
4694
+ for (const key of [
4695
+ "name",
4696
+ "super",
4697
+ "inherit",
4698
+ "createRole",
4699
+ "createDb",
4700
+ "canLogin",
4701
+ "replication",
4702
+ "connLimit",
4703
+ "validUntil",
4704
+ "bypassRls",
4705
+ "config"
4706
+ ]) {
4707
+ if (key === "name" && !to) continue;
4708
+ let value = ast[key];
4709
+ if (!compare && (!value || key === "connLimit" && value === -1)) {
4710
+ continue;
4711
+ }
4712
+ value = value instanceof Date ? `'${value.toISOString()}'` : key === "config" ? JSON.stringify(value) : typeof value === "string" ? singleQuote(value) : value;
4713
+ if (compare) {
4714
+ const a = value === -1 || value === false ? void 0 : value;
4715
+ const other = compare[key];
4716
+ const b = other === -1 || other === false ? void 0 : other;
4717
+ if (a === b) {
4718
+ continue;
4719
+ }
4720
+ }
4721
+ params.push(`${key}: ${value},`);
4527
4722
  }
4723
+ return params;
4528
4724
  };
4529
4725
  const isTimestamp = (column, type) => {
4530
4726
  if (!column) return false;
@@ -4932,7 +5128,19 @@ const collationsSql = (version) => `SELECT
4932
5128
  FROM pg_collation
4933
5129
  JOIN pg_namespace n on pg_collation.collnamespace = n.oid
4934
5130
  WHERE ${filterSchema("n.nspname")}`;
4935
- const sql = (version) => `SELECT (${schemasSql}) AS "schemas", ${jsonAgg(
5131
+ const roleSql = (params) => `SELECT json_agg(json_build_object(
5132
+ 'name', rolname,
5133
+ 'super', rolsuper,
5134
+ 'inherit', rolinherit,
5135
+ 'createRole', rolcreaterole,
5136
+ 'canLogin', rolcanlogin,
5137
+ 'replication', rolreplication,
5138
+ 'connLimit', rolconnlimit,
5139
+ 'validUntil', rolvaliduntil,
5140
+ 'bypassRls', rolbypassrls,
5141
+ 'config', rolconfig
5142
+ )) FROM pg_roles WHERE ${params.whereSql ?? `name != 'postgres' AND name !~ '^pg_'`}`;
5143
+ const sql = (version, params) => `SELECT (${schemasSql}) AS "schemas", ${jsonAgg(
4936
5144
  tablesSql,
4937
5145
  "tables"
4938
5146
  )}, ${jsonAgg(viewsSql, "views")}, ${jsonAgg(
@@ -4947,13 +5155,13 @@ const sql = (version) => `SELECT (${schemasSql}) AS "schemas", ${jsonAgg(
4947
5155
  )}, ${jsonAgg(domainsSql, "domains")}, ${jsonAgg(
4948
5156
  collationsSql(version),
4949
5157
  "collations"
4950
- )}`;
4951
- async function introspectDbSchema(db) {
5158
+ )}${params?.roles ? `, (${roleSql(params.roles)}) AS "roles"` : ""}`;
5159
+ async function introspectDbSchema(db, params) {
4952
5160
  const {
4953
5161
  rows: [{ version: versionString }]
4954
5162
  } = await db.query("SELECT version()");
4955
5163
  const version = +versionString.match(/\d+/)[0];
4956
- const data = await db.query(sql(version));
5164
+ const data = await db.query(sql(version, params));
4957
5165
  const result = data.rows[0];
4958
5166
  for (const domain of result.domains) {
4959
5167
  domain.checks = domain.checks?.filter((check) => check);
@@ -5032,6 +5240,26 @@ async function introspectDbSchema(db) {
5032
5240
  }
5033
5241
  result.indexes = indexes;
5034
5242
  result.excludes = excludes;
5243
+ if (result.roles) {
5244
+ for (const role of result.roles) {
5245
+ nullsToUndefined(role);
5246
+ if (role.validUntil) role.validUntil = new Date(role.validUntil);
5247
+ if (role.config) {
5248
+ const arr = role.config;
5249
+ role.config = Object.fromEntries(
5250
+ arr.map((item) => {
5251
+ const i = item.indexOf("=");
5252
+ const key = item.slice(0, i);
5253
+ const value = item.slice(i + 1);
5254
+ return [
5255
+ key,
5256
+ value[0] === '"' ? value.slice(1, -1).replaceAll('""', '"') : value
5257
+ ];
5258
+ })
5259
+ );
5260
+ }
5261
+ }
5262
+ }
5035
5263
  return result;
5036
5264
  }
5037
5265
  const nullsToUndefined = (obj) => {
@@ -5130,6 +5358,15 @@ const structureToAst = async (ctx, adapter, config) => {
5130
5358
  for (const view of data.views) {
5131
5359
  ast.push(viewToAst(ctx, data, domains, view));
5132
5360
  }
5361
+ if (data.roles) {
5362
+ for (const role of data.roles) {
5363
+ ast.push({
5364
+ type: "role",
5365
+ action: "create",
5366
+ ...role
5367
+ });
5368
+ }
5369
+ }
5133
5370
  return ast;
5134
5371
  };
5135
5372
  const makeDomainsMap = (ctx, data) => {
@@ -5296,7 +5533,8 @@ const getDbStructureTableData = (data, { name, schemaName }) => {
5296
5533
  } : void 0,
5297
5534
  indexes: data.indexes.filter(filterFn),
5298
5535
  excludes: data.excludes.filter(filterFn),
5299
- constraints
5536
+ constraints,
5537
+ roles: data.roles
5300
5538
  };
5301
5539
  };
5302
5540
  const filterByTableSchema = (tableName, schemaName) => (x) => x.tableName === tableName && x.schemaName === schemaName;