rake-db 2.30.7 → 2.30.8

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
@@ -788,7 +788,7 @@ const createTable = async (migration, up, tableName, first, second, third) => {
788
788
  shape = tableData = emptyObject;
789
789
  }
790
790
  const schema = migration.adapter.getSchema();
791
- const ast = makeAst$3(
791
+ const ast = makeAst$4(
792
792
  schema,
793
793
  up,
794
794
  tableName,
@@ -818,7 +818,7 @@ const createTable = async (migration, up, tableName, first, second, third) => {
818
818
  }
819
819
  };
820
820
  };
821
- const makeAst$3 = (schema, up, tableName, shape, tableData, options, noPrimaryKey) => {
821
+ const makeAst$4 = (schema, up, tableName, shape, tableData, options, noPrimaryKey) => {
822
822
  const shapePKeys = [];
823
823
  for (const key in shape) {
824
824
  const column = shape[key];
@@ -1168,7 +1168,7 @@ const changeTable = async (migration, up, tableName, options, fn) => {
1168
1168
  addOrDropChanges.length = 0;
1169
1169
  const changeData = fn?.(tableChanger) || {};
1170
1170
  const schema = migration.adapter.getSchema();
1171
- const ast = makeAst$2(
1171
+ const ast = makeAst$3(
1172
1172
  schema,
1173
1173
  up,
1174
1174
  tableName,
@@ -1182,7 +1182,7 @@ const changeTable = async (migration, up, tableName, options, fn) => {
1182
1182
  query.then?.(result);
1183
1183
  }
1184
1184
  };
1185
- const makeAst$2 = (schema, up, name, changeData, changeTableData2, options) => {
1185
+ const makeAst$3 = (schema, up, name, changeData, changeTableData2, options) => {
1186
1186
  const { comment } = options;
1187
1187
  const shape = {};
1188
1188
  const consumedChanges = {};
@@ -1634,11 +1634,11 @@ const renameColumnSql = (from, to) => {
1634
1634
 
1635
1635
  const createView = async (migration, up, name, options, sql) => {
1636
1636
  const schema = migration.adapter.getSchema();
1637
- const ast = makeAst$1(schema, up, name, options, sql);
1637
+ const ast = makeAst$2(schema, up, name, options, sql);
1638
1638
  const query = astToQuery$1(ast);
1639
1639
  await migration.adapter.arrays(interpolateSqlValues(query));
1640
1640
  };
1641
- const makeAst$1 = (schema, up, fullName, options, sql) => {
1641
+ const makeAst$2 = (schema, up, fullName, options, sql) => {
1642
1642
  if (typeof sql === "string") {
1643
1643
  sql = raw({ raw: sql });
1644
1644
  }
@@ -1703,11 +1703,11 @@ const serializers = {
1703
1703
  validUntil: (value) => `VALID UNTIL '${value === void 0 ? "infinity" : value}'`
1704
1704
  };
1705
1705
  const createOrDropRole = async (migration, up, name, params) => {
1706
- const ast = makeAst(up, name, params);
1706
+ const ast = makeAst$1(up, name, params);
1707
1707
  const sql = astToQuery(ast);
1708
1708
  await migration.adapter.arrays(sql);
1709
1709
  };
1710
- const makeAst = (up, name, params) => {
1710
+ const makeAst$1 = (up, name, params) => {
1711
1711
  return {
1712
1712
  type: "role",
1713
1713
  action: up ? "create" : "drop",
@@ -1828,24 +1828,154 @@ const renameAstToQuery = (ast) => {
1828
1828
  return `ALTER ROLE "${ast.from}" RENAME TO "${ast.to}"`;
1829
1829
  };
1830
1830
 
1831
+ const SCHEMA_OBJECT_TYPES = [
1832
+ "tables",
1833
+ "sequences",
1834
+ "functions",
1835
+ "types"
1836
+ ];
1837
+ const GLOBAL_ONLY_OBJECT_TYPES = ["schemas", "largeObjects"];
1838
+ const ALL_OBJECT_TYPES = [
1839
+ ...SCHEMA_OBJECT_TYPES,
1840
+ ...GLOBAL_ONLY_OBJECT_TYPES
1841
+ ];
1842
+ const objectTypeToSql = {
1843
+ tables: "TABLES",
1844
+ sequences: "SEQUENCES",
1845
+ functions: "FUNCTIONS",
1846
+ types: "TYPES",
1847
+ schemas: "SCHEMAS",
1848
+ largeObjects: "LARGE OBJECTS"
1849
+ };
1850
+ const processObjectPrivileges = (value) => {
1851
+ if (!value) return void 0;
1852
+ const { privileges, grantablePrivileges } = value;
1853
+ const grantableSet = new Set(grantablePrivileges ?? []);
1854
+ const filteredPrivileges = privileges?.filter((p) => !grantableSet.has(p));
1855
+ const result = {};
1856
+ if (filteredPrivileges?.length) {
1857
+ result.privileges = [...filteredPrivileges];
1858
+ }
1859
+ if (grantablePrivileges?.length) {
1860
+ result.grantablePrivileges = [...grantablePrivileges];
1861
+ }
1862
+ return Object.keys(result).length ? result : void 0;
1863
+ };
1864
+ const filterAndTransformConfig = (config, schema) => {
1865
+ if (!config) return void 0;
1866
+ const objectTypes = schema ? SCHEMA_OBJECT_TYPES : ALL_OBJECT_TYPES;
1867
+ const result = {};
1868
+ if (config.allGrantable) {
1869
+ for (const key of objectTypes) {
1870
+ result[key] = { grantablePrivileges: ["ALL"] };
1871
+ }
1872
+ } else if (config.all) {
1873
+ for (const key of objectTypes) {
1874
+ result[key] = { privileges: ["ALL"] };
1875
+ }
1876
+ }
1877
+ for (const key of ALL_OBJECT_TYPES) {
1878
+ const value = config[key];
1879
+ if (!value) continue;
1880
+ const processed = processObjectPrivileges(value);
1881
+ if (processed) {
1882
+ result[key] = processed;
1883
+ }
1884
+ }
1885
+ return Object.keys(result).length ? result : void 0;
1886
+ };
1887
+ const changeDefaultPrivileges = async (migration, up, arg) => {
1888
+ const ast = makeAst(up, arg);
1889
+ const sql = astToSql(ast);
1890
+ if (sql.length) {
1891
+ await migration.adapter.arrays(sql.join(";\n"));
1892
+ }
1893
+ };
1894
+ const makeAst = (up, arg) => {
1895
+ if (!up) {
1896
+ const { grant, revoke } = arg;
1897
+ arg = {
1898
+ ...arg,
1899
+ grant: revoke,
1900
+ revoke: grant
1901
+ };
1902
+ }
1903
+ return {
1904
+ type: "defaultPrivilege",
1905
+ owner: arg.owner,
1906
+ grantee: arg.grantee,
1907
+ schema: arg.schema,
1908
+ grant: filterAndTransformConfig(arg.grant, arg.schema),
1909
+ revoke: filterAndTransformConfig(arg.revoke, arg.schema)
1910
+ };
1911
+ };
1912
+ const astToSql = (ast) => {
1913
+ const queries = [];
1914
+ if (ast.grant) {
1915
+ queries.push(...objectConfigToSql(ast, ast.grant, "GRANT"));
1916
+ }
1917
+ if (ast.revoke) {
1918
+ queries.push(...objectConfigToSql(ast, ast.revoke, "REVOKE"));
1919
+ }
1920
+ return queries;
1921
+ };
1922
+ const objectConfigToSql = (ast, config, action) => {
1923
+ const queries = [];
1924
+ for (const [key, value] of Object.entries(config)) {
1925
+ const objectType = key;
1926
+ if (!value) continue;
1927
+ const { privileges, grantablePrivileges } = value;
1928
+ if (privileges?.length) {
1929
+ queries.push(buildQuery(ast, objectType, privileges, false, action));
1930
+ }
1931
+ if (grantablePrivileges?.length) {
1932
+ queries.push(
1933
+ buildQuery(ast, objectType, grantablePrivileges, true, action)
1934
+ );
1935
+ }
1936
+ }
1937
+ return queries;
1938
+ };
1939
+ const buildQuery = (ast, objectType, privileges, grantable, action) => {
1940
+ const parts = ["ALTER DEFAULT PRIVILEGES"];
1941
+ if (ast.owner) {
1942
+ parts.push(`FOR ROLE "${ast.owner}"`);
1943
+ }
1944
+ if (ast.schema) {
1945
+ parts.push(`IN SCHEMA "${ast.schema}"`);
1946
+ }
1947
+ const privilegeList = privileges.map((p) => p === "ALL" ? "ALL PRIVILEGES" : p).join(", ");
1948
+ if (action === "GRANT") {
1949
+ parts.push(
1950
+ `GRANT ${privilegeList} ON ${objectTypeToSql[objectType]} TO "${ast.grantee}"`
1951
+ );
1952
+ if (grantable) {
1953
+ parts.push("WITH GRANT OPTION");
1954
+ }
1955
+ } else {
1956
+ parts.push(
1957
+ `REVOKE ${privilegeList} ON ${objectTypeToSql[objectType]} FROM "${ast.grantee}"`
1958
+ );
1959
+ }
1960
+ return parts.join(" ");
1961
+ };
1962
+
1831
1963
  const createMigrationInterface = (tx, up, config) => {
1832
1964
  const adapter = Object.create(tx);
1833
1965
  const { query, arrays } = adapter;
1834
1966
  const log = logParamToLogObject(config.logger || console, config.log);
1835
1967
  adapter.query = (text, values) => {
1836
- return wrapWithLog(
1837
- log,
1968
+ return wrapWithEnhancingError(
1838
1969
  text,
1839
1970
  values,
1840
- () => query.call(adapter, text, values)
1971
+ wrapWithLog(log, text, values, () => query.call(adapter, text, values))
1841
1972
  );
1842
1973
  };
1843
1974
  adapter.arrays = (text, values) => {
1844
- return wrapWithLog(
1845
- log,
1975
+ return wrapWithEnhancingError(
1846
1976
  text,
1847
1977
  values,
1848
- () => arrays.call(adapter, text, values)
1978
+ wrapWithLog(log, text, values, () => arrays.call(adapter, text, values))
1849
1979
  );
1850
1980
  };
1851
1981
  Object.assign(adapter, { silentQuery: query, silentArrays: arrays });
@@ -2729,7 +2859,23 @@ class Migration {
2729
2859
  params.to
2730
2860
  );
2731
2861
  }
2862
+ changeDefaultPrivileges(params) {
2863
+ return changeDefaultPrivileges(this, this.up, params);
2864
+ }
2732
2865
  }
2866
+ const wrapWithEnhancingError = async (text, values, promise) => {
2867
+ try {
2868
+ return await promise;
2869
+ } catch (err) {
2870
+ if (err && typeof err === "object" && "message" in err && typeof err.message === "string" && err.constructor.name === "PostgresError") {
2871
+ err.message += `
2872
+ SQL: ${text}${values ? `
2873
+ Variables: ${JSON.stringify(values)}` : ""}`;
2874
+ throw new err.constructor(err);
2875
+ }
2876
+ throw err;
2877
+ }
2878
+ };
2733
2879
  const wrapWithLog = async (log, text, values, fn) => {
2734
2880
  if (!log) {
2735
2881
  return fn();
@@ -4099,9 +4245,24 @@ const astToGenerateItem = (config, ast, currentSchema) => {
4099
4245
  deps.push(tableSchema, `${tableSchema}.${tableName}`);
4100
4246
  break;
4101
4247
  }
4102
- case "role":
4103
- case "changeRole":
4248
+ case "role": {
4249
+ deps.push(`role:${ast.name}`);
4250
+ break;
4251
+ }
4252
+ case "changeRole": {
4253
+ deps.push(`role:${ast.name}`);
4254
+ break;
4255
+ }
4104
4256
  case "renameRole": {
4257
+ drop.push(ast.from);
4258
+ add.push(ast.to);
4259
+ deps.push(`role:${ast.from}`, `role:${ast.to}`);
4260
+ break;
4261
+ }
4262
+ case "defaultPrivilege": {
4263
+ if (ast.schema) deps.push(ast.schema);
4264
+ if (ast.owner) deps.push(`role:${ast.owner}`);
4265
+ deps.push(`role:${ast.grantee}`);
4105
4266
  break;
4106
4267
  }
4107
4268
  default:
@@ -4709,6 +4870,69 @@ const astEncoders = {
4709
4870
  [...from.length ? ["from: {", from, "},"] : [], "to: {", to, "},"],
4710
4871
  "});"
4711
4872
  ];
4873
+ },
4874
+ defaultPrivilege(ast) {
4875
+ const code = [`await db.changeDefaultPrivileges({`];
4876
+ const props = [];
4877
+ if (ast.owner) {
4878
+ props.push(`owner: ${singleQuote(ast.owner)},`);
4879
+ }
4880
+ props.push(`grantee: ${singleQuote(ast.grantee)},`);
4881
+ if (ast.schema) {
4882
+ props.push(`schema: ${singleQuote(ast.schema)},`);
4883
+ }
4884
+ const objectConfigToCode = (config) => {
4885
+ const result = [];
4886
+ for (const key of [
4887
+ "tables",
4888
+ "sequences",
4889
+ "functions",
4890
+ "types",
4891
+ "schemas",
4892
+ "largeObjects"
4893
+ ]) {
4894
+ const value = config?.[key];
4895
+ if (!value) continue;
4896
+ const { privileges, grantablePrivileges } = value;
4897
+ const hasPrivileges = privileges?.length;
4898
+ const hasGrantable = grantablePrivileges?.length;
4899
+ if (!hasPrivileges && !hasGrantable) continue;
4900
+ result.push(`${key}: {`);
4901
+ const lines = [];
4902
+ if (hasPrivileges) {
4903
+ lines.push(
4904
+ `privileges: [${privileges.map(singleQuote).join(", ")}],`
4905
+ );
4906
+ }
4907
+ if (hasGrantable) {
4908
+ lines.push(
4909
+ `grantablePrivileges: [${grantablePrivileges.map(singleQuote).join(", ")}],`
4910
+ );
4911
+ }
4912
+ result.push(lines);
4913
+ result.push(`},`);
4914
+ }
4915
+ return result;
4916
+ };
4917
+ if (ast.grant) {
4918
+ const grantCode = objectConfigToCode(ast.grant);
4919
+ if (grantCode.length) {
4920
+ props.push("grant: {");
4921
+ props.push(grantCode);
4922
+ props.push("},");
4923
+ }
4924
+ }
4925
+ if (ast.revoke) {
4926
+ const revokeCode = objectConfigToCode(ast.revoke);
4927
+ if (revokeCode.length) {
4928
+ props.push("revoke: {");
4929
+ props.push(revokeCode);
4930
+ props.push("},");
4931
+ }
4932
+ }
4933
+ code.push(props);
4934
+ addCode(code, "});");
4935
+ return code;
4712
4936
  }
4713
4937
  };
4714
4938
  const roleParams = (name, ast, compare, to) => {
@@ -5163,6 +5387,25 @@ const roleSql = (params) => `SELECT COALESCE(json_agg(json_build_object(
5163
5387
  'bypassRls', rolbypassrls,
5164
5388
  'config', rolconfig
5165
5389
  )), '[]') FROM pg_roles WHERE ${params.whereSql ?? `rolname != 'postgres' AND rolname !~ '^pg_'`}`;
5390
+ const defaultPrivilegesSql = `SELECT COALESCE(json_agg(t.*), '[]') FROM (
5391
+ SELECT
5392
+ (ae.grantor)::regrole "grantor",
5393
+ (ae.grantee)::regrole "grantee",
5394
+ (SELECT nspname FROM pg_namespace n WHERE n.oid = d.defaclnamespace) "schema",
5395
+ CASE d.defaclobjtype
5396
+ WHEN 'r' THEN 'relation'
5397
+ WHEN 'S' THEN 'sequence'
5398
+ WHEN 'f' THEN 'function'
5399
+ WHEN 'T' THEN 'type'
5400
+ WHEN 'n' THEN 'schema'
5401
+ WHEN 'L' THEN 'large_object'
5402
+ END "object",
5403
+ array_agg(ae.privilege_type) "privileges",
5404
+ array_agg(ae.is_grantable) "isGrantables"
5405
+ FROM pg_default_acl d
5406
+ JOIN LATERAL aclexplode(d.defaclacl) ae ON true
5407
+ GROUP BY "grantor", "grantee", "schema", "object"
5408
+ ) t`;
5166
5409
  const sql = (version, params) => `SELECT (${schemasSql}) AS "schemas", ${jsonAgg(
5167
5410
  tablesSql,
5168
5411
  "tables"
@@ -5178,19 +5421,22 @@ const sql = (version, params) => `SELECT (${schemasSql}) AS "schemas", ${jsonAgg
5178
5421
  )}, ${jsonAgg(domainsSql, "domains")}, ${jsonAgg(
5179
5422
  collationsSql(version),
5180
5423
  "collations"
5181
- )}${params?.roles ? `, (${roleSql(params.roles)}) AS "roles"` : ""}`;
5182
- async function introspectDbSchema(db, params) {
5424
+ )}${params?.roles ? `, (${roleSql(params.roles)}) AS "roles"` : ""}${params?.loadDefaultPrivileges ? `, (${defaultPrivilegesSql}) AS "defaultPrivileges"` : ""}`;
5425
+ async function getDbVersion(db) {
5183
5426
  const {
5184
5427
  rows: [{ version: versionString }]
5185
5428
  } = await db.query("SELECT version()");
5186
- const version = +versionString.match(/\d+/)[0];
5429
+ return +versionString.match(/\d+/)[0];
5430
+ }
5431
+ async function introspectDbSchema(db, params) {
5432
+ const version = await getDbVersion(db);
5187
5433
  const data = await db.query(sql(version, params));
5188
- const result = data.rows[0];
5189
- for (const domain of result.domains) {
5434
+ const raw = data.rows[0];
5435
+ for (const domain of raw.domains) {
5190
5436
  domain.checks = domain.checks?.filter((check) => check);
5191
5437
  nullsToUndefined(domain);
5192
5438
  }
5193
- for (const table of result.tables) {
5439
+ for (const table of raw.tables) {
5194
5440
  for (const column of table.columns) {
5195
5441
  nullsToUndefined(column);
5196
5442
  if (column.identity) nullsToUndefined(column.identity);
@@ -5201,7 +5447,7 @@ async function introspectDbSchema(db, params) {
5201
5447
  }
5202
5448
  const indexes = [];
5203
5449
  const excludes = [];
5204
- for (const index of result.indexes) {
5450
+ for (const index of raw.indexes) {
5205
5451
  nullsToUndefined(index);
5206
5452
  for (const column of index.columns) {
5207
5453
  if (!("expression" in column)) continue;
@@ -5261,10 +5507,10 @@ async function introspectDbSchema(db, params) {
5261
5507
  }
5262
5508
  (index.exclude ? excludes : indexes).push(index);
5263
5509
  }
5264
- result.indexes = indexes;
5265
- result.excludes = excludes;
5266
- if (result.roles) {
5267
- for (const role of result.roles) {
5510
+ raw.indexes = indexes;
5511
+ raw.excludes = excludes;
5512
+ if (raw.roles) {
5513
+ for (const role of raw.roles) {
5268
5514
  nullsToUndefined(role);
5269
5515
  if (role.validUntil) {
5270
5516
  role.validUntil = role.validUntil === "infinity" ? void 0 : new Date(role.validUntil);
@@ -5285,7 +5531,41 @@ async function introspectDbSchema(db, params) {
5285
5531
  }
5286
5532
  }
5287
5533
  }
5288
- return result;
5534
+ return {
5535
+ version,
5536
+ ...raw,
5537
+ defaultPrivileges: raw.defaultPrivileges && [
5538
+ ...raw.defaultPrivileges.reduce((acc, privilege) => {
5539
+ nullsToUndefined(privilege);
5540
+ const key = `${privilege.grantor}.${privilege.grantee}.${privilege.schema}`;
5541
+ const existing = acc.get(key);
5542
+ const objectType = privilege.object === "relation" ? "TABLES" : privilege.object === "sequence" ? "SEQUENCES" : privilege.object === "function" ? "FUNCTIONS" : privilege.object === "type" ? "TYPES" : privilege.object === "schema" ? "SCHEMAS" : privilege.object === "large_object" ? "LARGE_OBJECTS" : null;
5543
+ if (!objectType) {
5544
+ throw new Error(
5545
+ `Unknown default privilege object type: ${privilege.object}`
5546
+ );
5547
+ }
5548
+ const objectConfig = {
5549
+ object: objectType,
5550
+ privilegeConfigs: privilege.privileges.map((priv, i) => ({
5551
+ privilege: priv,
5552
+ isGrantable: privilege.isGrantables[i]
5553
+ }))
5554
+ };
5555
+ if (existing) {
5556
+ existing.objectConfigs.push(objectConfig);
5557
+ } else {
5558
+ acc.set(key, {
5559
+ owner: privilege.grantor,
5560
+ grantee: privilege.grantee,
5561
+ schema: privilege.schema,
5562
+ objectConfigs: [objectConfig]
5563
+ });
5564
+ }
5565
+ return acc;
5566
+ }, /* @__PURE__ */ new Map()).values()
5567
+ ]
5568
+ };
5289
5569
  }
5290
5570
  const nullsToUndefined = (obj) => {
5291
5571
  for (const key in obj) {
@@ -6374,5 +6654,5 @@ for (const key in rakeDbAliases) {
6374
6654
  if (command) rakeDbCommands[key] = rakeDbCommands[command];
6375
6655
  }
6376
6656
 
6377
- export { RakeDbError, astToMigration, concatSchemaAndName, createDatabase, createMigrationInterface, createMigrationsSchemaAndTable, createSchema$1 as createSchema, createTable$1 as createTable, dbColumnToAst, dropDatabase, dropSchema, dropTable, encodeColumnDefault, getConstraintName, getDbStructureTableData, getDbTableColumnsChecks, getExcludeName, getIndexName, getMigrationsSchemaAndTable, getSchemaAndTableFromName, incrementIntermediateCaller, instantiateDbColumn, introspectDbSchema, makeDomainsMap, makeFileVersion, makeRakeDbConfig, makeStructureToAstCtx, migrate, migrateAndClose, migrationConfigDefaults, promptSelect, rakeDbCliWithAdapter, rakeDbCommands, redo, rollback, runMigration, saveMigratedVersion, setRakeDbCliRunFn, structureToAst, tableToAst, writeMigrationFile };
6657
+ export { RakeDbError, astToMigration, concatSchemaAndName, createDatabase, createMigrationInterface, createMigrationsSchemaAndTable, createSchema$1 as createSchema, createTable$1 as createTable, dbColumnToAst, dropDatabase, dropSchema, dropTable, encodeColumnDefault, getConstraintName, getDbStructureTableData, getDbTableColumnsChecks, getDbVersion, getExcludeName, getIndexName, getMigrationsSchemaAndTable, getSchemaAndTableFromName, incrementIntermediateCaller, instantiateDbColumn, introspectDbSchema, makeDomainsMap, makeFileVersion, makeRakeDbConfig, makeStructureToAstCtx, migrate, migrateAndClose, migrationConfigDefaults, promptSelect, rakeDbCliWithAdapter, rakeDbCommands, redo, rollback, runMigration, saveMigratedVersion, setRakeDbCliRunFn, structureToAst, tableToAst, writeMigrationFile };
6378
6658
  //# sourceMappingURL=index.mjs.map