orchid-orm 1.70.1 → 1.71.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.d.ts +14 -6
- package/dist/index.js +6 -0
- package/dist/index.js.map +1 -1
- package/dist/index.mjs +6 -1
- package/dist/index.mjs.map +1 -1
- package/dist/migrations/index.js +691 -16
- package/dist/migrations/index.js.map +1 -1
- package/dist/migrations/index.mjs +691 -16
- package/dist/migrations/index.mjs.map +1 -1
- package/dist/node-postgres.d.ts +7 -3
- package/dist/postgres-js.d.ts +6 -4
- package/package.json +6 -6
|
@@ -1313,28 +1313,42 @@ const dropCheck = ({ changeTableAst: { drop }, changingColumns }, dbCheck, name)
|
|
|
1313
1313
|
};
|
|
1314
1314
|
const defaultRlsState = {
|
|
1315
1315
|
enable: false,
|
|
1316
|
-
force:
|
|
1316
|
+
force: true
|
|
1317
1317
|
};
|
|
1318
1318
|
const normalizeRlsFlag = (value, defaultValue) => {
|
|
1319
1319
|
if (value === void 0) return defaultValue;
|
|
1320
1320
|
return value === true || value === "true" || value === "t";
|
|
1321
1321
|
};
|
|
1322
|
-
const processTableRls = (ast, dbStructure, tables, currentSchema) => {
|
|
1322
|
+
const processTableRls = async (adapter, ast, dbStructure, tables, currentSchema, generatorIgnore) => {
|
|
1323
1323
|
const projectRlsDefaults = tables[0]?.internal.rls?.tableRlsDefaults;
|
|
1324
|
+
const ignore = generatorIgnore;
|
|
1325
|
+
const ignoredTableSet = toIgnoredTableSet(ignore?.tables, currentSchema);
|
|
1326
|
+
const ignoredRlsTableSet = toIgnoredTableSet(ignore?.rls?.tables, currentSchema);
|
|
1327
|
+
const ignoredPolicyMap = toIgnoredPolicyMap(ignore?.rls?.policies, currentSchema);
|
|
1324
1328
|
for (const table of tables) {
|
|
1325
1329
|
const tableRls = table.internal.tableRls;
|
|
1326
1330
|
if (!tableRls) continue;
|
|
1327
1331
|
const schemaName = table.q.schema ?? currentSchema;
|
|
1332
|
+
const tableId = `${schemaName}.${table.table}`;
|
|
1333
|
+
if (ignoredTableSet.has(tableId) || ignoredRlsTableSet.has(tableId)) continue;
|
|
1328
1334
|
const dbTable = dbStructure.tables.find((item) => item.schemaName === schemaName && item.name === table.table);
|
|
1329
|
-
|
|
1335
|
+
const ignoredPolicyNames = ignoredPolicyMap.get(tableId);
|
|
1330
1336
|
const codeRls = {
|
|
1331
1337
|
enable: normalizeRlsFlag(tableRls.enable ?? projectRlsDefaults?.enable, defaultRlsState.enable),
|
|
1332
1338
|
force: normalizeRlsFlag(tableRls.force ?? projectRlsDefaults?.force, defaultRlsState.force)
|
|
1333
1339
|
};
|
|
1334
1340
|
const dbRls = {
|
|
1335
|
-
enable: normalizeRlsFlag(dbTable
|
|
1336
|
-
force: normalizeRlsFlag(dbTable
|
|
1341
|
+
enable: normalizeRlsFlag(dbTable?.rls?.enable, defaultRlsState.enable),
|
|
1342
|
+
force: normalizeRlsFlag(dbTable?.rls?.force, false)
|
|
1337
1343
|
};
|
|
1344
|
+
const policyExpressionComparisons = [];
|
|
1345
|
+
const policyChanges = collectPolicyChanges(schemaName, table.table, normalizeCodePolicies(tableRls, ignoredPolicyNames), normalizeDbPolicies(dbTable?.rls?.policies, ignoredPolicyNames), policyExpressionComparisons, concatSchemaAndName({
|
|
1346
|
+
schema: schemaName,
|
|
1347
|
+
name: table.table
|
|
1348
|
+
}));
|
|
1349
|
+
if (policyExpressionComparisons.length) await compareSqlExpressions(policyExpressionComparisons, adapter);
|
|
1350
|
+
const disableFirst = !codeRls.enable && dbRls.enable;
|
|
1351
|
+
if (!disableFirst) pushPolicyChanges(ast, policyChanges);
|
|
1338
1352
|
if (codeRls.enable !== dbRls.enable) ast.push({
|
|
1339
1353
|
type: "tableRls",
|
|
1340
1354
|
action: codeRls.enable ? "enable" : "disable",
|
|
@@ -1347,7 +1361,250 @@ const processTableRls = (ast, dbStructure, tables, currentSchema) => {
|
|
|
1347
1361
|
schema: schemaName,
|
|
1348
1362
|
table: table.table
|
|
1349
1363
|
});
|
|
1364
|
+
if (disableFirst) pushPolicyChanges(ast, policyChanges);
|
|
1365
|
+
}
|
|
1366
|
+
};
|
|
1367
|
+
const toIgnoredTableSet = (tables, currentSchema) => {
|
|
1368
|
+
const ignored = /* @__PURE__ */ new Set();
|
|
1369
|
+
if (!tables) return ignored;
|
|
1370
|
+
for (const name of tables) {
|
|
1371
|
+
const [schema = currentSchema, table] = getSchemaAndTableFromName(currentSchema, name);
|
|
1372
|
+
ignored.add(`${schema}.${table}`);
|
|
1373
|
+
}
|
|
1374
|
+
return ignored;
|
|
1375
|
+
};
|
|
1376
|
+
const toIgnoredPolicyMap = (items, currentSchema) => {
|
|
1377
|
+
const result = /* @__PURE__ */ new Map();
|
|
1378
|
+
if (!items) return result;
|
|
1379
|
+
for (const item of items) {
|
|
1380
|
+
const [schema = currentSchema, table] = getSchemaAndTableFromName(currentSchema, item.table);
|
|
1381
|
+
const key = `${schema}.${table}`;
|
|
1382
|
+
let set = result.get(key);
|
|
1383
|
+
if (!set) {
|
|
1384
|
+
set = /* @__PURE__ */ new Set();
|
|
1385
|
+
result.set(key, set);
|
|
1386
|
+
}
|
|
1387
|
+
for (const name of item.names) set.add(name);
|
|
1388
|
+
}
|
|
1389
|
+
return result;
|
|
1390
|
+
};
|
|
1391
|
+
const normalizeCodePolicies = (tableRls, ignoredPolicyNames) => {
|
|
1392
|
+
const result = [];
|
|
1393
|
+
const permit = tableRls.permit ?? [];
|
|
1394
|
+
const restrict = tableRls.restrict ?? [];
|
|
1395
|
+
for (const policy of permit) {
|
|
1396
|
+
const normalized = normalizeCodePolicy("PERMISSIVE", policy);
|
|
1397
|
+
if (normalized && !ignoredPolicyNames?.has(normalized.name)) result.push(normalized);
|
|
1398
|
+
}
|
|
1399
|
+
for (const policy of restrict) {
|
|
1400
|
+
const normalized = normalizeCodePolicy("RESTRICTIVE", policy);
|
|
1401
|
+
if (normalized && !ignoredPolicyNames?.has(normalized.name)) result.push(normalized);
|
|
1350
1402
|
}
|
|
1403
|
+
return result;
|
|
1404
|
+
};
|
|
1405
|
+
const normalizeCodePolicy = (as, policy) => {
|
|
1406
|
+
if (!policy?.name) return;
|
|
1407
|
+
return {
|
|
1408
|
+
name: policy.name,
|
|
1409
|
+
as,
|
|
1410
|
+
for: policy.for ?? "ALL",
|
|
1411
|
+
to: normalizePolicyRoles(policy.to),
|
|
1412
|
+
using: toSqlText(policy.using),
|
|
1413
|
+
withCheck: toSqlText(policy.withCheck)
|
|
1414
|
+
};
|
|
1415
|
+
};
|
|
1416
|
+
const normalizeDbPolicies = (policies, ignoredPolicyNames) => {
|
|
1417
|
+
if (!policies) return [];
|
|
1418
|
+
const result = [];
|
|
1419
|
+
for (const policy of policies) {
|
|
1420
|
+
if (ignoredPolicyNames?.has(policy.name)) continue;
|
|
1421
|
+
result.push({
|
|
1422
|
+
name: policy.name,
|
|
1423
|
+
as: policy.mode,
|
|
1424
|
+
for: policy.command,
|
|
1425
|
+
to: normalizePolicyRoles(policy.roles),
|
|
1426
|
+
using: policy.using,
|
|
1427
|
+
withCheck: policy.withCheck
|
|
1428
|
+
});
|
|
1429
|
+
}
|
|
1430
|
+
return result;
|
|
1431
|
+
};
|
|
1432
|
+
const normalizePolicyRoles = (roles) => {
|
|
1433
|
+
const result = (Array.isArray(roles) ? roles : roles ? [roles] : ["public"]).map((role) => role.toLowerCase() === "public" ? "public" : role);
|
|
1434
|
+
return result.length ? result : ["public"];
|
|
1435
|
+
};
|
|
1436
|
+
const toSqlText = (value) => {
|
|
1437
|
+
if (!value) return;
|
|
1438
|
+
return value.toSQL({ values: [] });
|
|
1439
|
+
};
|
|
1440
|
+
const collectPolicyChanges = (schema, table, codePolicies, dbPolicies, compareExpressions, source) => {
|
|
1441
|
+
const changes = [];
|
|
1442
|
+
const dbPolicyMap = /* @__PURE__ */ new Map();
|
|
1443
|
+
for (const policy of dbPolicies) dbPolicyMap.set(policy.name, policy);
|
|
1444
|
+
const codePolicyMap = /* @__PURE__ */ new Map();
|
|
1445
|
+
for (const policy of codePolicies) codePolicyMap.set(policy.name, policy);
|
|
1446
|
+
const unmatchedCodePolicies = [];
|
|
1447
|
+
const unmatchedDbPolicies = [];
|
|
1448
|
+
for (const policy of codePolicies) {
|
|
1449
|
+
const dbPolicy = dbPolicyMap.get(policy.name);
|
|
1450
|
+
if (!dbPolicy) {
|
|
1451
|
+
unmatchedCodePolicies.push(policy);
|
|
1452
|
+
continue;
|
|
1453
|
+
}
|
|
1454
|
+
const recreate = policy.as !== dbPolicy.as || policy.for !== dbPolicy.for;
|
|
1455
|
+
const toChanged = !isSameStringArray(policy.to, dbPolicy.to);
|
|
1456
|
+
const from = {};
|
|
1457
|
+
const to = {};
|
|
1458
|
+
const codeUsing = policy.using;
|
|
1459
|
+
const dbUsing = dbPolicy.using;
|
|
1460
|
+
const codeWithCheck = policy.withCheck;
|
|
1461
|
+
const dbWithCheck = dbPolicy.withCheck;
|
|
1462
|
+
const hasUsing = codeUsing !== void 0 || dbUsing !== void 0;
|
|
1463
|
+
const hasWithCheck = codeWithCheck !== void 0 || dbWithCheck !== void 0;
|
|
1464
|
+
if (toChanged) {
|
|
1465
|
+
from.to = dbPolicy.to;
|
|
1466
|
+
to.to = policy.to;
|
|
1467
|
+
}
|
|
1468
|
+
const usingCanCompare = codeUsing !== void 0 && dbUsing !== void 0;
|
|
1469
|
+
const withCheckCanCompare = codeWithCheck !== void 0 && dbWithCheck !== void 0;
|
|
1470
|
+
const applyExpressionDiff = (matched) => {
|
|
1471
|
+
const usingChanged = usingCanCompare ? !matched : hasUsing && !usingCanCompare;
|
|
1472
|
+
const withCheckChanged = withCheckCanCompare ? !matched : hasWithCheck && !withCheckCanCompare;
|
|
1473
|
+
if (usingChanged) {
|
|
1474
|
+
from.using = dbPolicy.using;
|
|
1475
|
+
to.using = policy.using;
|
|
1476
|
+
}
|
|
1477
|
+
if (withCheckChanged) {
|
|
1478
|
+
from.withCheck = dbPolicy.withCheck;
|
|
1479
|
+
to.withCheck = policy.withCheck;
|
|
1480
|
+
}
|
|
1481
|
+
if (recreate) {
|
|
1482
|
+
changes.push({
|
|
1483
|
+
type: "changePolicy",
|
|
1484
|
+
schema,
|
|
1485
|
+
table,
|
|
1486
|
+
name: policy.name,
|
|
1487
|
+
from: {
|
|
1488
|
+
as: dbPolicy.as,
|
|
1489
|
+
for: dbPolicy.for,
|
|
1490
|
+
to: dbPolicy.to,
|
|
1491
|
+
using: dbPolicy.using,
|
|
1492
|
+
withCheck: dbPolicy.withCheck
|
|
1493
|
+
},
|
|
1494
|
+
to: {
|
|
1495
|
+
as: policy.as,
|
|
1496
|
+
for: policy.for,
|
|
1497
|
+
to: policy.to,
|
|
1498
|
+
using: policy.using,
|
|
1499
|
+
withCheck: policy.withCheck
|
|
1500
|
+
}
|
|
1501
|
+
});
|
|
1502
|
+
return;
|
|
1503
|
+
}
|
|
1504
|
+
if (!Object.keys(from).length) return;
|
|
1505
|
+
changes.push({
|
|
1506
|
+
type: "changePolicy",
|
|
1507
|
+
schema,
|
|
1508
|
+
table,
|
|
1509
|
+
name: policy.name,
|
|
1510
|
+
from,
|
|
1511
|
+
to
|
|
1512
|
+
});
|
|
1513
|
+
};
|
|
1514
|
+
if (!hasUsing && !hasWithCheck) {
|
|
1515
|
+
applyExpressionDiff(true);
|
|
1516
|
+
continue;
|
|
1517
|
+
}
|
|
1518
|
+
const compare = [];
|
|
1519
|
+
if (usingCanCompare) compare.push({
|
|
1520
|
+
inDb: dbUsing,
|
|
1521
|
+
inCode: [codeUsing]
|
|
1522
|
+
});
|
|
1523
|
+
if (withCheckCanCompare) compare.push({
|
|
1524
|
+
inDb: dbWithCheck,
|
|
1525
|
+
inCode: [codeWithCheck]
|
|
1526
|
+
});
|
|
1527
|
+
if (!compare.length) {
|
|
1528
|
+
applyExpressionDiff(false);
|
|
1529
|
+
continue;
|
|
1530
|
+
}
|
|
1531
|
+
compareExpressions.push({
|
|
1532
|
+
source,
|
|
1533
|
+
compare,
|
|
1534
|
+
handle(i) {
|
|
1535
|
+
applyExpressionDiff(i !== void 0);
|
|
1536
|
+
}
|
|
1537
|
+
});
|
|
1538
|
+
}
|
|
1539
|
+
for (const policy of dbPolicies) {
|
|
1540
|
+
if (codePolicyMap.has(policy.name)) continue;
|
|
1541
|
+
unmatchedDbPolicies.push(policy);
|
|
1542
|
+
}
|
|
1543
|
+
const pairedDbPolicyNames = /* @__PURE__ */ new Set();
|
|
1544
|
+
const pairedCodePolicyNames = /* @__PURE__ */ new Set();
|
|
1545
|
+
for (const policy of unmatchedCodePolicies) {
|
|
1546
|
+
const renamedFrom = unmatchedDbPolicies.find((item) => !pairedDbPolicyNames.has(item.name) && item.as === policy.as && item.for === policy.for);
|
|
1547
|
+
if (!renamedFrom) continue;
|
|
1548
|
+
pairedDbPolicyNames.add(renamedFrom.name);
|
|
1549
|
+
pairedCodePolicyNames.add(policy.name);
|
|
1550
|
+
changes.push({
|
|
1551
|
+
type: "changePolicy",
|
|
1552
|
+
schema,
|
|
1553
|
+
table,
|
|
1554
|
+
name: renamedFrom.name,
|
|
1555
|
+
from: {
|
|
1556
|
+
name: renamedFrom.name,
|
|
1557
|
+
to: renamedFrom.to,
|
|
1558
|
+
using: renamedFrom.using,
|
|
1559
|
+
withCheck: renamedFrom.withCheck
|
|
1560
|
+
},
|
|
1561
|
+
to: {
|
|
1562
|
+
name: policy.name,
|
|
1563
|
+
to: policy.to,
|
|
1564
|
+
using: policy.using,
|
|
1565
|
+
withCheck: policy.withCheck
|
|
1566
|
+
}
|
|
1567
|
+
});
|
|
1568
|
+
}
|
|
1569
|
+
for (const policy of unmatchedCodePolicies) {
|
|
1570
|
+
if (pairedCodePolicyNames.has(policy.name)) continue;
|
|
1571
|
+
changes.push({
|
|
1572
|
+
type: "policy",
|
|
1573
|
+
action: "create",
|
|
1574
|
+
schema,
|
|
1575
|
+
table,
|
|
1576
|
+
name: policy.name,
|
|
1577
|
+
as: policy.as,
|
|
1578
|
+
for: policy.for,
|
|
1579
|
+
to: policy.to,
|
|
1580
|
+
using: policy.using,
|
|
1581
|
+
withCheck: policy.withCheck
|
|
1582
|
+
});
|
|
1583
|
+
}
|
|
1584
|
+
for (const policy of unmatchedDbPolicies) {
|
|
1585
|
+
if (pairedDbPolicyNames.has(policy.name)) continue;
|
|
1586
|
+
changes.push({
|
|
1587
|
+
type: "policy",
|
|
1588
|
+
action: "drop",
|
|
1589
|
+
schema,
|
|
1590
|
+
table,
|
|
1591
|
+
name: policy.name,
|
|
1592
|
+
as: policy.as,
|
|
1593
|
+
for: policy.for,
|
|
1594
|
+
to: policy.to,
|
|
1595
|
+
using: policy.using,
|
|
1596
|
+
withCheck: policy.withCheck
|
|
1597
|
+
});
|
|
1598
|
+
}
|
|
1599
|
+
return changes;
|
|
1600
|
+
};
|
|
1601
|
+
const pushPolicyChanges = (ast, changes) => {
|
|
1602
|
+
for (const change of changes) ast.push(change);
|
|
1603
|
+
};
|
|
1604
|
+
const isSameStringArray = (a, b) => {
|
|
1605
|
+
if (a.length !== b.length) return false;
|
|
1606
|
+
for (let i = 0; i < a.length; i++) if (a[i] !== b[i]) return false;
|
|
1607
|
+
return true;
|
|
1351
1608
|
};
|
|
1352
1609
|
const processTables = async (ast, domainsMap, adapter, dbStructure, config, { structureToAstCtx, codeItems: { tables }, currentSchema, internal: { generatorIgnore }, verifying }, pendingDbTypes) => {
|
|
1353
1610
|
const createTables = collectCreateTables(tables, dbStructure, currentSchema);
|
|
@@ -1362,7 +1619,7 @@ const processTables = async (ast, domainsMap, adapter, dbStructure, config, { st
|
|
|
1362
1619
|
await applyChangeTables(adapter, changeTables, structureToAstCtx, dbStructure, domainsMap, ast, currentSchema, config, compareSql, tableExpressions, verifying, pendingDbTypes);
|
|
1363
1620
|
processForeignKeys(config, ast, changeTables, currentSchema, tableShapes);
|
|
1364
1621
|
await Promise.all([applyCompareSql(compareSql, adapter), compareSqlExpressions(tableExpressions, adapter)]);
|
|
1365
|
-
processTableRls(ast, dbStructure, tables, currentSchema);
|
|
1622
|
+
await processTableRls(adapter, ast, dbStructure, tables, currentSchema, generatorIgnore);
|
|
1366
1623
|
for (const dbTable of dropTables) ast.push(tableToAst(structureToAstCtx, dbStructure, dbTable, "drop", domainsMap));
|
|
1367
1624
|
};
|
|
1368
1625
|
const collectCreateTables = (tables, dbStructure, currentSchema) => {
|
|
@@ -1722,7 +1979,7 @@ const privilegeConfigsMatch = (a, b, objectType, objectTypeToAllPrivileges) => {
|
|
|
1722
1979
|
if (b.privilege === "ALL" && a.isGrantable === b.isGrantable) return expectedPrivs.includes(a.privilege);
|
|
1723
1980
|
return false;
|
|
1724
1981
|
};
|
|
1725
|
-
const normalizeRoleName = (name) => {
|
|
1982
|
+
const normalizeRoleName$1 = (name) => {
|
|
1726
1983
|
if (name.startsWith("\"") && name.endsWith("\"")) return name.slice(1, -1);
|
|
1727
1984
|
return name;
|
|
1728
1985
|
};
|
|
@@ -1792,7 +2049,7 @@ const processDefaultPrivileges = (ast, dbStructure, { internal: { roles } }) =>
|
|
|
1792
2049
|
}
|
|
1793
2050
|
const found = /* @__PURE__ */ new Set();
|
|
1794
2051
|
for (const dbPrivilege of dbStructure.defaultPrivileges) {
|
|
1795
|
-
const grantee = normalizeRoleName(dbPrivilege.grantee);
|
|
2052
|
+
const grantee = normalizeRoleName$1(dbPrivilege.grantee);
|
|
1796
2053
|
if (grantee === "postgres") continue;
|
|
1797
2054
|
const key = `${grantee}.${dbPrivilege.schema}`;
|
|
1798
2055
|
const codePrivilege = codePrivileges.get(key);
|
|
@@ -1868,6 +2125,280 @@ const processDefaultPrivileges = (ast, dbStructure, { internal: { roles } }) =>
|
|
|
1868
2125
|
});
|
|
1869
2126
|
}
|
|
1870
2127
|
};
|
|
2128
|
+
const targetKeys = [
|
|
2129
|
+
"schemas",
|
|
2130
|
+
"tables",
|
|
2131
|
+
"sequences",
|
|
2132
|
+
"routines",
|
|
2133
|
+
"types",
|
|
2134
|
+
"domains",
|
|
2135
|
+
"databases"
|
|
2136
|
+
];
|
|
2137
|
+
const schemaWideTargetKeys = [
|
|
2138
|
+
"allTablesIn",
|
|
2139
|
+
"allSequencesIn",
|
|
2140
|
+
"allRoutinesIn"
|
|
2141
|
+
];
|
|
2142
|
+
const supportedPrivileges = {
|
|
2143
|
+
schemas: ["USAGE", "CREATE"],
|
|
2144
|
+
tables: [
|
|
2145
|
+
"SELECT",
|
|
2146
|
+
"INSERT",
|
|
2147
|
+
"UPDATE",
|
|
2148
|
+
"DELETE",
|
|
2149
|
+
"TRUNCATE",
|
|
2150
|
+
"REFERENCES",
|
|
2151
|
+
"TRIGGER",
|
|
2152
|
+
"MAINTAIN"
|
|
2153
|
+
],
|
|
2154
|
+
sequences: [
|
|
2155
|
+
"USAGE",
|
|
2156
|
+
"SELECT",
|
|
2157
|
+
"UPDATE"
|
|
2158
|
+
],
|
|
2159
|
+
routines: ["EXECUTE"],
|
|
2160
|
+
types: ["USAGE"],
|
|
2161
|
+
domains: ["USAGE"],
|
|
2162
|
+
databases: [
|
|
2163
|
+
"CREATE",
|
|
2164
|
+
"CONNECT",
|
|
2165
|
+
"TEMPORARY"
|
|
2166
|
+
]
|
|
2167
|
+
};
|
|
2168
|
+
const schemaWideToConcreteTarget = {
|
|
2169
|
+
allTablesIn: "tables",
|
|
2170
|
+
allSequencesIn: "sequences",
|
|
2171
|
+
allRoutinesIn: "routines"
|
|
2172
|
+
};
|
|
2173
|
+
const processGrants = (ast, dbStructure, params) => {
|
|
2174
|
+
const { grants } = params.internal;
|
|
2175
|
+
if (!grants || !dbStructure.grants) return;
|
|
2176
|
+
const codeStates = collectCodeGrants(dbStructure, params).filter((grant) => !isIgnoredGrant(grant, params));
|
|
2177
|
+
const dbStates = collectDbGrants(dbStructure, params.currentSchema).filter((grant) => !isIgnoredGrant(grant, params));
|
|
2178
|
+
const managedTargets = getManagedGrantTargets(params);
|
|
2179
|
+
for (const code of codeStates) {
|
|
2180
|
+
const actual = dbStates.filter((db) => isSameGrantTarget(code, db));
|
|
2181
|
+
addGrantAst(ast, "grant", code, missingPrivileges(code.grantablePrivileges, actual, "grantablePrivileges"), "grantablePrivileges");
|
|
2182
|
+
addGrantAst(ast, "revoke", code, grantOptionsToRevoke(code.privileges, code.grantablePrivileges, actual), "grantablePrivileges");
|
|
2183
|
+
addGrantAst(ast, "grant", code, missingPrivileges(code.privileges, actual, "privileges"), "privileges");
|
|
2184
|
+
}
|
|
2185
|
+
for (const actual of dbStates) {
|
|
2186
|
+
const configured = codeStates.filter((code) => isSameGrantTarget(code, actual));
|
|
2187
|
+
if (!shouldRevokeActualGrant(actual, configured, managedTargets)) continue;
|
|
2188
|
+
const revokeGrant = getRevokeGrantState(actual, configured);
|
|
2189
|
+
addGrantAst(ast, "revoke", revokeGrant, privilegesToRevoke(actual.privileges, configured), "privileges");
|
|
2190
|
+
addGrantAst(ast, "revoke", revokeGrant, privilegesToRevoke(actual.grantablePrivileges, configured), "grantablePrivileges");
|
|
2191
|
+
}
|
|
2192
|
+
};
|
|
2193
|
+
const getManagedGrantTargets = ({ codeItems, currentSchema }) => {
|
|
2194
|
+
const targets = {
|
|
2195
|
+
tables: /* @__PURE__ */ new Set(),
|
|
2196
|
+
domains: /* @__PURE__ */ new Set(),
|
|
2197
|
+
types: /* @__PURE__ */ new Set()
|
|
2198
|
+
};
|
|
2199
|
+
for (const table of codeItems.tables) targets.tables.add(`${table.q.schema ?? currentSchema}.${table.table}`);
|
|
2200
|
+
for (const domain of codeItems.domains) targets.domains.add(`${domain.schemaName}.${domain.name}`);
|
|
2201
|
+
for (const enumItem of codeItems.enums.values()) targets.types.add(`${enumItem.schema ?? currentSchema}.${enumItem.name}`);
|
|
2202
|
+
return targets;
|
|
2203
|
+
};
|
|
2204
|
+
const shouldRevokeActualGrant = (actual, configured, managedTargets) => {
|
|
2205
|
+
if (configured.length) return true;
|
|
2206
|
+
if (actual.targetKey === "tables") return managedTargets.tables.has(actual.target);
|
|
2207
|
+
if (actual.targetKey === "domains") return managedTargets.domains.has(actual.target);
|
|
2208
|
+
if (actual.targetKey === "types") return managedTargets.types.has(actual.target);
|
|
2209
|
+
return false;
|
|
2210
|
+
};
|
|
2211
|
+
const getRevokeGrantState = (actual, configured) => {
|
|
2212
|
+
if (configured.some((grant) => grant.grantedBy)) return actual;
|
|
2213
|
+
return {
|
|
2214
|
+
...actual,
|
|
2215
|
+
grantedBy: void 0
|
|
2216
|
+
};
|
|
2217
|
+
};
|
|
2218
|
+
const collectCodeGrants = (dbStructure, { currentSchema, internal }) => {
|
|
2219
|
+
const states = [];
|
|
2220
|
+
for (const grant of internal.grants ?? []) {
|
|
2221
|
+
for (const targetKey of targetKeys) {
|
|
2222
|
+
const values = grant[targetKey];
|
|
2223
|
+
if (!values?.length) continue;
|
|
2224
|
+
addStates(states, withEffectiveGrantor(grant, internal), targetKey, values, currentSchema);
|
|
2225
|
+
}
|
|
2226
|
+
for (const targetKey of schemaWideTargetKeys) {
|
|
2227
|
+
const values = grant[targetKey];
|
|
2228
|
+
if (!values?.length) continue;
|
|
2229
|
+
const concreteTargetKey = schemaWideToConcreteTarget[targetKey];
|
|
2230
|
+
addStates(states, withEffectiveGrantor(grant, internal), concreteTargetKey, getSchemaWideTargets(dbStructure, concreteTargetKey, values), currentSchema);
|
|
2231
|
+
}
|
|
2232
|
+
}
|
|
2233
|
+
return states;
|
|
2234
|
+
};
|
|
2235
|
+
const collectDbGrants = (dbStructure, currentSchema) => {
|
|
2236
|
+
const states = [];
|
|
2237
|
+
for (const grant of dbStructure.grants ?? []) for (const targetKey of targetKeys) {
|
|
2238
|
+
const values = grant[targetKey];
|
|
2239
|
+
if (!values?.length) continue;
|
|
2240
|
+
addStates(states, grant, targetKey, values, currentSchema);
|
|
2241
|
+
}
|
|
2242
|
+
return states;
|
|
2243
|
+
};
|
|
2244
|
+
const withEffectiveGrantor = (grant, internal) => {
|
|
2245
|
+
return {
|
|
2246
|
+
...grant,
|
|
2247
|
+
grantedBy: grant.grantedBy ?? internal.defaultGrantedBy
|
|
2248
|
+
};
|
|
2249
|
+
};
|
|
2250
|
+
const isIgnoredGrant = (grant, { currentSchema, internal: { generatorIgnore } }) => {
|
|
2251
|
+
const { grants } = generatorIgnore ?? {};
|
|
2252
|
+
if (matchesSelector(grants?.roles, grant.to)) return true;
|
|
2253
|
+
const names = getGrantTargetNames(grant, currentSchema);
|
|
2254
|
+
if (matchesSelector(grants?.[grant.targetKey], names)) return true;
|
|
2255
|
+
if (grant.targetKey === "tables" && names.schema && matchesSelector(grants?.allTablesIn, names.schema)) return true;
|
|
2256
|
+
if (grant.targetKey === "sequences" && names.schema && matchesSelector(grants?.allSequencesIn, names.schema)) return true;
|
|
2257
|
+
if (grant.targetKey === "routines" && names.schema && matchesSelector(grants?.allRoutinesIn, names.schema)) return true;
|
|
2258
|
+
return isTopLevelIgnoredGrant(grant, names, generatorIgnore);
|
|
2259
|
+
};
|
|
2260
|
+
const isTopLevelIgnoredGrant = (grant, names, generatorIgnore) => {
|
|
2261
|
+
if (grant.targetKey === "schemas") return !!generatorIgnore?.schemas?.includes(grant.target);
|
|
2262
|
+
if (names.schema && generatorIgnore?.schemas?.includes(names.schema)) return true;
|
|
2263
|
+
if (grant.targetKey === "tables") return isIgnoredByName(generatorIgnore?.tables, names);
|
|
2264
|
+
if (grant.targetKey === "domains") return isIgnoredByName(generatorIgnore?.domains, names);
|
|
2265
|
+
return false;
|
|
2266
|
+
};
|
|
2267
|
+
const getGrantTargetNames = (grant, currentSchema) => {
|
|
2268
|
+
if (grant.targetKey === "schemas" || grant.targetKey === "databases") return {
|
|
2269
|
+
name: grant.target,
|
|
2270
|
+
qualified: grant.target
|
|
2271
|
+
};
|
|
2272
|
+
const [schema, name] = getSchemaAndTableFromName(currentSchema, grant.target);
|
|
2273
|
+
return {
|
|
2274
|
+
schema,
|
|
2275
|
+
name,
|
|
2276
|
+
qualified: schema ? `${schema}.${name}` : name
|
|
2277
|
+
};
|
|
2278
|
+
};
|
|
2279
|
+
const isIgnoredByName = (ignored, names) => {
|
|
2280
|
+
return !!ignored?.some((name) => name === names.qualified || name === names.name || (names.schema ? name === `${names.schema}.${names.name}` : false));
|
|
2281
|
+
};
|
|
2282
|
+
const matchesSelector = (selector, value) => {
|
|
2283
|
+
if (!selector) return false;
|
|
2284
|
+
const values = typeof value === "string" ? [value] : [
|
|
2285
|
+
value.qualified,
|
|
2286
|
+
value.name,
|
|
2287
|
+
value.schema
|
|
2288
|
+
].filter(isString);
|
|
2289
|
+
return (Array.isArray(selector) ? selector : [selector]).some((item) => values.some((name) => typeof item === "string" ? item === name : item.test(name)));
|
|
2290
|
+
};
|
|
2291
|
+
const isString = (value) => {
|
|
2292
|
+
return !!value;
|
|
2293
|
+
};
|
|
2294
|
+
const addStates = (states, grant, targetKey, values, currentSchema) => {
|
|
2295
|
+
for (const to of grant.to) for (const value of values) {
|
|
2296
|
+
const target = typeof value === "string" ? normalizeTarget(targetKey, value, currentSchema) : value.target;
|
|
2297
|
+
addOrMergeState(states, {
|
|
2298
|
+
targetKey,
|
|
2299
|
+
target,
|
|
2300
|
+
outputTarget: typeof value === "string" ? normalizeOutputTarget(target, currentSchema) : value.outputTarget,
|
|
2301
|
+
to: normalizeRoleName(to),
|
|
2302
|
+
grantedBy: grant.grantedBy ? normalizeRoleName(grant.grantedBy) : void 0,
|
|
2303
|
+
privileges: expandPrivileges(targetKey, grant.privileges),
|
|
2304
|
+
grantablePrivileges: expandPrivileges(targetKey, grant.grantablePrivileges)
|
|
2305
|
+
});
|
|
2306
|
+
}
|
|
2307
|
+
};
|
|
2308
|
+
const addOrMergeState = (states, state) => {
|
|
2309
|
+
const existing = states.find((item) => isSameExactGrantTarget(item, state));
|
|
2310
|
+
if (!existing) {
|
|
2311
|
+
removeGrantableFromOrdinary(state);
|
|
2312
|
+
states.push(state);
|
|
2313
|
+
return;
|
|
2314
|
+
}
|
|
2315
|
+
for (const privilege of state.privileges) if (!existing.grantablePrivileges.has(privilege)) existing.privileges.add(privilege);
|
|
2316
|
+
for (const privilege of state.grantablePrivileges) {
|
|
2317
|
+
existing.grantablePrivileges.add(privilege);
|
|
2318
|
+
existing.privileges.delete(privilege);
|
|
2319
|
+
}
|
|
2320
|
+
};
|
|
2321
|
+
const removeGrantableFromOrdinary = (state) => {
|
|
2322
|
+
for (const privilege of state.grantablePrivileges) state.privileges.delete(privilege);
|
|
2323
|
+
};
|
|
2324
|
+
const normalizeRoleName = (name) => {
|
|
2325
|
+
return name.startsWith("\"") && name.endsWith("\"") ? name.slice(1, -1) : name;
|
|
2326
|
+
};
|
|
2327
|
+
const getSchemaWideTargets = (dbStructure, targetKey, schemas) => {
|
|
2328
|
+
const selectedSchemas = new Set(schemas);
|
|
2329
|
+
if (targetKey === "tables") return [...dbStructure.tables.map((table) => ({
|
|
2330
|
+
target: `${table.schemaName}.${table.name}`,
|
|
2331
|
+
outputTarget: `${table.schemaName}.${table.name}`
|
|
2332
|
+
})), ...dbStructure.views.map((view) => ({
|
|
2333
|
+
target: `${view.schemaName}.${view.name}`,
|
|
2334
|
+
outputTarget: `${view.schemaName}.${view.name}`
|
|
2335
|
+
}))].filter((item) => selectedSchemas.has(item.target.split(".")[0]));
|
|
2336
|
+
if (targetKey === "sequences" || targetKey === "routines") return getSchemaWideGrantTargets(dbStructure, targetKey, selectedSchemas);
|
|
2337
|
+
return [];
|
|
2338
|
+
};
|
|
2339
|
+
const getSchemaWideGrantTargets = (dbStructure, targetKey, selectedSchemas) => {
|
|
2340
|
+
const targets = /* @__PURE__ */ new Map();
|
|
2341
|
+
for (const grant of dbStructure.grants ?? []) for (const target of grant[targetKey] ?? []) {
|
|
2342
|
+
const [schema] = target.split(".");
|
|
2343
|
+
if (!selectedSchemas.has(schema)) continue;
|
|
2344
|
+
targets.set(target, {
|
|
2345
|
+
target,
|
|
2346
|
+
outputTarget: target
|
|
2347
|
+
});
|
|
2348
|
+
}
|
|
2349
|
+
return [...targets.values()];
|
|
2350
|
+
};
|
|
2351
|
+
const normalizeTarget = (targetKey, value, currentSchema) => {
|
|
2352
|
+
if (targetKey === "schemas" || targetKey === "databases") return value;
|
|
2353
|
+
const [schema, name] = getSchemaAndTableFromName(currentSchema, value);
|
|
2354
|
+
return `${schema ?? currentSchema}.${name}`;
|
|
2355
|
+
};
|
|
2356
|
+
const normalizeOutputTarget = (target, currentSchema) => {
|
|
2357
|
+
const [schema, name] = target.split(".");
|
|
2358
|
+
if (!name) return target;
|
|
2359
|
+
return schema === currentSchema ? name : target;
|
|
2360
|
+
};
|
|
2361
|
+
const expandPrivileges = (targetKey, privileges) => {
|
|
2362
|
+
const set = /* @__PURE__ */ new Set();
|
|
2363
|
+
for (const privilege of privileges ?? []) if (privilege === "ALL") for (const supported of supportedPrivileges[targetKey]) set.add(supported);
|
|
2364
|
+
else set.add(privilege === "TEMP" ? "TEMPORARY" : privilege);
|
|
2365
|
+
return set;
|
|
2366
|
+
};
|
|
2367
|
+
const isSameGrantTarget = (a, b) => {
|
|
2368
|
+
return a.targetKey === b.targetKey && a.target === b.target && a.to === b.to && (!a.grantedBy || !b.grantedBy || a.grantedBy === b.grantedBy);
|
|
2369
|
+
};
|
|
2370
|
+
const isSameExactGrantTarget = (a, b) => {
|
|
2371
|
+
return a.targetKey === b.targetKey && a.target === b.target && a.to === b.to && a.grantedBy === b.grantedBy;
|
|
2372
|
+
};
|
|
2373
|
+
const missingPrivileges = (expected, actual, key) => {
|
|
2374
|
+
const result = [];
|
|
2375
|
+
for (const privilege of expected) if (!actual.some((state) => state[key].has(privilege))) result.push(privilege);
|
|
2376
|
+
return result;
|
|
2377
|
+
};
|
|
2378
|
+
const grantOptionsToRevoke = (expectedOrdinary, expectedGrantable, actual) => {
|
|
2379
|
+
const result = [];
|
|
2380
|
+
for (const privilege of expectedOrdinary) {
|
|
2381
|
+
if (expectedGrantable.has(privilege)) continue;
|
|
2382
|
+
if (actual.some((state) => state.grantablePrivileges.has(privilege))) result.push(privilege);
|
|
2383
|
+
}
|
|
2384
|
+
return result;
|
|
2385
|
+
};
|
|
2386
|
+
const privilegesToRevoke = (actual, configured) => {
|
|
2387
|
+
const result = [];
|
|
2388
|
+
for (const privilege of actual) if (!configured.some((state) => state.privileges.has(privilege) || state.grantablePrivileges.has(privilege))) result.push(privilege);
|
|
2389
|
+
return result;
|
|
2390
|
+
};
|
|
2391
|
+
const addGrantAst = (ast, action, grant, privileges, privilegeKey) => {
|
|
2392
|
+
if (!privileges.length) return;
|
|
2393
|
+
ast.push({
|
|
2394
|
+
type: "grant",
|
|
2395
|
+
action,
|
|
2396
|
+
to: [grant.to],
|
|
2397
|
+
[grant.targetKey]: [grant.outputTarget],
|
|
2398
|
+
[privilegeKey]: privileges,
|
|
2399
|
+
grantedBy: grant.grantedBy
|
|
2400
|
+
});
|
|
2401
|
+
};
|
|
1871
2402
|
/**
|
|
1872
2403
|
* This is needed to compare SQLs of table expressions.
|
|
1873
2404
|
* Need to exclude table columns of pending types, such as enums or domains,
|
|
@@ -1886,6 +2417,7 @@ const composeMigration = async (adapter, config, ast, dbStructure, params) => {
|
|
|
1886
2417
|
const { structureToAstCtx, currentSchema } = params;
|
|
1887
2418
|
await processRoles(ast, dbStructure, params);
|
|
1888
2419
|
processDefaultPrivileges(ast, dbStructure, params);
|
|
2420
|
+
processGrants(ast, dbStructure, params);
|
|
1889
2421
|
const domainsMap = makeDomainsMap(structureToAstCtx, dbStructure);
|
|
1890
2422
|
await processSchemas(ast, dbStructure, params);
|
|
1891
2423
|
processExtensions(ast, dbStructure, params);
|
|
@@ -1896,7 +2428,7 @@ const composeMigration = async (adapter, config, ast, dbStructure, params) => {
|
|
|
1896
2428
|
return astToMigration(currentSchema, config, ast);
|
|
1897
2429
|
};
|
|
1898
2430
|
const rollbackErr = /* @__PURE__ */ new Error("Rollback");
|
|
1899
|
-
const verifyMigration = async (adapter, config, migrationCode, generateMigrationParams, roles,
|
|
2431
|
+
const verifyMigration = async (adapter, config, migrationCode, generateMigrationParams, roles, structureParams) => {
|
|
1900
2432
|
const migrationFn = new Function("change", migrationCode);
|
|
1901
2433
|
let code;
|
|
1902
2434
|
try {
|
|
@@ -1913,7 +2445,7 @@ const verifyMigration = async (adapter, config, migrationCode, generateMigration
|
|
|
1913
2445
|
const dbStructure = await introspectDbSchema(trx, {
|
|
1914
2446
|
rls: generateMigrationParams.codeItems.tables.some((table) => !!table.internal.tableRls),
|
|
1915
2447
|
roles,
|
|
1916
|
-
|
|
2448
|
+
...structureParams
|
|
1917
2449
|
});
|
|
1918
2450
|
generateMigrationParams.verifying = true;
|
|
1919
2451
|
try {
|
|
@@ -2099,6 +2631,14 @@ const report = (ast, config, currentSchema) => {
|
|
|
2099
2631
|
if (parts.length) code.push(parts.join("\n"));
|
|
2100
2632
|
break;
|
|
2101
2633
|
}
|
|
2634
|
+
case "grant": {
|
|
2635
|
+
const parts = [];
|
|
2636
|
+
const target = grantTargetToReport(a, currentSchema);
|
|
2637
|
+
if (a.privileges?.length) parts.push(`${a.action === "grant" ? green("+ grant privileges") : red("- revoke privileges")} ${a.privileges.map(mapGrantPrivilege).join(", ")} on ${target} ${a.action === "grant" ? "to" : "from"} ${a.to.join(", ")}`);
|
|
2638
|
+
if (a.grantablePrivileges?.length) parts.push(`${a.action === "grant" ? green("+ grant privileges") : red("- revoke privileges")} ${a.grantablePrivileges.map(mapGrantPrivilege).join(", ")} on ${target} with grant option ${a.action === "grant" ? "to" : "from"} ${a.to.join(", ")}`);
|
|
2639
|
+
if (parts.length) code.push(parts.join("\n"));
|
|
2640
|
+
break;
|
|
2641
|
+
}
|
|
2102
2642
|
case "tableRls": {
|
|
2103
2643
|
const table = dbItemName({
|
|
2104
2644
|
schema: a.schema,
|
|
@@ -2108,6 +2648,28 @@ const report = (ast, config, currentSchema) => {
|
|
|
2108
2648
|
code.push(message);
|
|
2109
2649
|
break;
|
|
2110
2650
|
}
|
|
2651
|
+
case "policy": {
|
|
2652
|
+
const summary = formatPolicySummary(a, dbItemName({
|
|
2653
|
+
schema: a.schema,
|
|
2654
|
+
name: a.table
|
|
2655
|
+
}, currentSchema));
|
|
2656
|
+
const message = a.action === "create" ? `${green("+ create policy")} ${a.name}: ${summary}` : `${red("- drop policy")} ${a.name}: ${summary}`;
|
|
2657
|
+
code.push(message);
|
|
2658
|
+
break;
|
|
2659
|
+
}
|
|
2660
|
+
case "changePolicy": {
|
|
2661
|
+
const table = dbItemName({
|
|
2662
|
+
schema: a.schema,
|
|
2663
|
+
name: a.table
|
|
2664
|
+
}, currentSchema);
|
|
2665
|
+
const fromName = a.from.name ?? a.name;
|
|
2666
|
+
const toName = a.to.name ?? a.name;
|
|
2667
|
+
const message = fromName === toName ? `${yellow("~ change policy")} ${a.name} on ${table}:` : `${yellow("~ rename policy")} ${fromName} ${yellow("=>")} ${toName} on ${table}:`;
|
|
2668
|
+
const inner = [`${yellow("from")}: ${formatPolicyChangeDefinition(a.from)}`, `${yellow("to")}: ${formatPolicyChangeDefinition(a.to)}`];
|
|
2669
|
+
code.push(message);
|
|
2670
|
+
code.push(inner);
|
|
2671
|
+
break;
|
|
2672
|
+
}
|
|
2111
2673
|
default: exhaustive(a);
|
|
2112
2674
|
}
|
|
2113
2675
|
const result = codeToString(code, "", " ");
|
|
@@ -2116,6 +2678,64 @@ const report = (ast, config, currentSchema) => {
|
|
|
2116
2678
|
const dbItemName = ({ schema, name }, currentSchema) => {
|
|
2117
2679
|
return schema && schema !== currentSchema ? `${schema}.${name}` : name;
|
|
2118
2680
|
};
|
|
2681
|
+
const grantTargetKeys = [
|
|
2682
|
+
"schemas",
|
|
2683
|
+
"tables",
|
|
2684
|
+
"sequences",
|
|
2685
|
+
"routines",
|
|
2686
|
+
"types",
|
|
2687
|
+
"domains",
|
|
2688
|
+
"databases"
|
|
2689
|
+
];
|
|
2690
|
+
const mapGrantPrivilege = (privilege) => {
|
|
2691
|
+
return privilege === "ALL" ? "ALL PRIVILEGES" : privilege;
|
|
2692
|
+
};
|
|
2693
|
+
const grantTargetToReport = (grant, currentSchema) => {
|
|
2694
|
+
for (const key of grantTargetKeys) {
|
|
2695
|
+
const targets = grant[key];
|
|
2696
|
+
if (!targets?.length) continue;
|
|
2697
|
+
return `${key} ${targets.map((target) => formatGrantTarget(key, target, currentSchema)).join(", ")}`;
|
|
2698
|
+
}
|
|
2699
|
+
return "unknown target";
|
|
2700
|
+
};
|
|
2701
|
+
const formatGrantTarget = (key, target, currentSchema) => {
|
|
2702
|
+
if (key === "schemas" || key === "databases") return target;
|
|
2703
|
+
const [schema, name] = getSchemaAndTableFromName(currentSchema, target);
|
|
2704
|
+
return dbItemName({
|
|
2705
|
+
schema,
|
|
2706
|
+
name
|
|
2707
|
+
}, currentSchema);
|
|
2708
|
+
};
|
|
2709
|
+
const formatPolicyRoles = (roles) => {
|
|
2710
|
+
return roles?.length ? roles.join(", ") : "public";
|
|
2711
|
+
};
|
|
2712
|
+
const formatPolicyCommand = (command) => {
|
|
2713
|
+
return (command ?? "ALL").toLowerCase();
|
|
2714
|
+
};
|
|
2715
|
+
const formatPolicyMode = (mode) => {
|
|
2716
|
+
return mode === "PERMISSIVE" ? "permit" : "restrict";
|
|
2717
|
+
};
|
|
2718
|
+
const formatPolicySummary = (policy, table) => {
|
|
2719
|
+
const parts = [
|
|
2720
|
+
`${formatPolicyMode(policy.as)} access on ${table}`,
|
|
2721
|
+
`to ${formatPolicyRoles(policy.to)}`,
|
|
2722
|
+
`for ${formatPolicyCommand(policy.for)}`
|
|
2723
|
+
];
|
|
2724
|
+
if (policy.using) parts.push(`using (${policy.using})`);
|
|
2725
|
+
if (policy.withCheck) parts.push(`with check (${policy.withCheck})`);
|
|
2726
|
+
return parts.join(", ");
|
|
2727
|
+
};
|
|
2728
|
+
const formatPolicyChangeDefinition = (policy) => {
|
|
2729
|
+
const parts = [];
|
|
2730
|
+
if (policy.name) parts.push(`name ${policy.name}`);
|
|
2731
|
+
if (policy.table) parts.push(`table ${policy.table}`);
|
|
2732
|
+
if (policy.as) parts.push(`as ${policy.as.toLowerCase()}`);
|
|
2733
|
+
if (policy.for) parts.push(`for ${policy.for.toLowerCase()}`);
|
|
2734
|
+
if ("to" in policy) parts.push(`to ${formatPolicyRoles(policy.to)}`);
|
|
2735
|
+
if ("using" in policy && policy.using) parts.push(`using (${policy.using})`);
|
|
2736
|
+
if ("withCheck" in policy && policy.withCheck) parts.push(`with check (${policy.withCheck})`);
|
|
2737
|
+
return parts.join(", ");
|
|
2738
|
+
};
|
|
2119
2739
|
var AbortSignal = class extends Error {};
|
|
2120
2740
|
const generate = async (adapters, config, args, afterPull) => {
|
|
2121
2741
|
let { dbPath } = config;
|
|
@@ -2131,17 +2751,25 @@ const generate = async (adapters, config, args, afterPull) => {
|
|
|
2131
2751
|
if (afterPull) adapters = [afterPull.adapter];
|
|
2132
2752
|
const db = await getDbFromConfig(config, dbPath);
|
|
2133
2753
|
const { columnTypes, internal } = db.$qb;
|
|
2754
|
+
const structureParams = {
|
|
2755
|
+
loadDefaultPrivileges: internal.roles?.some((role) => role.defaultPrivileges !== void 0) ?? false,
|
|
2756
|
+
loadGrants: !!internal.grants || hasCodeTablesWithGrants(db)
|
|
2757
|
+
};
|
|
2134
2758
|
const rolesDbStructureParam = internal.roles ? internal.managedRolesSql ? { whereSql: internal.managedRolesSql } : emptyObject : void 0;
|
|
2135
|
-
const { dbStructure } = await migrateAndPullStructures(adapters, config, db, rolesDbStructureParam,
|
|
2759
|
+
const { dbStructure } = await migrateAndPullStructures(adapters, config, db, rolesDbStructureParam, structureParams, afterPull);
|
|
2136
2760
|
const [adapter] = adapters;
|
|
2137
2761
|
const adapterSchema = adapter.getSchema();
|
|
2138
2762
|
const currentSchema = (typeof adapterSchema === "function" ? adapterSchema() : adapterSchema) ?? "public";
|
|
2139
2763
|
const codeItems = await getActualItems(db, currentSchema, internal, columnTypes);
|
|
2764
|
+
const effectiveGrants = getEffectiveGrants(internal.grants, codeItems);
|
|
2140
2765
|
const generateMigrationParams = {
|
|
2141
2766
|
structureToAstCtx: makeStructureToAstCtx(config, currentSchema),
|
|
2142
2767
|
codeItems,
|
|
2143
2768
|
currentSchema,
|
|
2144
|
-
internal
|
|
2769
|
+
internal: {
|
|
2770
|
+
...internal,
|
|
2771
|
+
grants: effectiveGrants
|
|
2772
|
+
}
|
|
2145
2773
|
};
|
|
2146
2774
|
const ast = [];
|
|
2147
2775
|
let migrationCode;
|
|
@@ -2155,7 +2783,7 @@ const generate = async (adapters, config, args, afterPull) => {
|
|
|
2155
2783
|
throw err;
|
|
2156
2784
|
}
|
|
2157
2785
|
if (migrationCode && !afterPull) {
|
|
2158
|
-
const result = await verifyMigration(adapter, config, migrationCode, generateMigrationParams, rolesDbStructureParam,
|
|
2786
|
+
const result = await verifyMigration(adapter, config, migrationCode, generateMigrationParams, rolesDbStructureParam, structureParams);
|
|
2159
2787
|
if (result !== void 0) throw new Error(`Failed to verify generated migration: some of database changes were not applied properly. This is a bug, please open an issue, attach the following migration code:\n${migrationCode}${result === false ? "" : `\nAfter applying:\n${result}`}`);
|
|
2160
2788
|
}
|
|
2161
2789
|
const { logger } = config;
|
|
@@ -2184,7 +2812,7 @@ const getDbFromConfig = async (config, dbPath) => {
|
|
|
2184
2812
|
if (!db?.$qb) throw new Error(`Unable to import OrchidORM instance as ${config.dbExportedAs ?? "db"} from ${config.dbPath}`);
|
|
2185
2813
|
return db;
|
|
2186
2814
|
};
|
|
2187
|
-
const migrateAndPullStructures = async (adapters, config, db, roles,
|
|
2815
|
+
const migrateAndPullStructures = async (adapters, config, db, roles, structureParams, afterPull) => {
|
|
2188
2816
|
if (afterPull) return { dbStructure: {
|
|
2189
2817
|
version: await getDbVersion(adapters[0]),
|
|
2190
2818
|
schemas: [],
|
|
@@ -2203,7 +2831,8 @@ const migrateAndPullStructures = async (adapters, config, db, roles, defaultPriv
|
|
|
2203
2831
|
const dbStructures = await Promise.all(adapters.map((adapter) => introspectDbSchema(adapter, {
|
|
2204
2832
|
rls: hasCodeTablesWithRls(db),
|
|
2205
2833
|
roles,
|
|
2206
|
-
loadDefaultPrivileges:
|
|
2834
|
+
loadDefaultPrivileges: structureParams?.loadDefaultPrivileges,
|
|
2835
|
+
loadGrants: structureParams?.loadGrants
|
|
2207
2836
|
})));
|
|
2208
2837
|
const dbStructure = dbStructures[0];
|
|
2209
2838
|
for (let i = 1; i < dbStructures.length; i++) compareDbStructures(dbStructure, dbStructures[i], i);
|
|
@@ -2216,6 +2845,30 @@ const hasCodeTablesWithRls = (db) => {
|
|
|
2216
2845
|
}
|
|
2217
2846
|
return false;
|
|
2218
2847
|
};
|
|
2848
|
+
const hasCodeTablesWithGrants = (db) => {
|
|
2849
|
+
for (const key in db) {
|
|
2850
|
+
if (key[0] === "$") continue;
|
|
2851
|
+
if (db[key].internal.tableGrants?.length) return true;
|
|
2852
|
+
}
|
|
2853
|
+
return false;
|
|
2854
|
+
};
|
|
2855
|
+
const getEffectiveGrants = (grants, codeItems) => {
|
|
2856
|
+
const effectiveGrants = grants ? [...grants] : [];
|
|
2857
|
+
for (const table of codeItems.tables) {
|
|
2858
|
+
const tableGrants = table.internal.tableGrants;
|
|
2859
|
+
if (!tableGrants?.length) continue;
|
|
2860
|
+
const tableTarget = table.q.schema ? `${table.q.schema}.${table.table}` : table.table;
|
|
2861
|
+
for (const grant of tableGrants) {
|
|
2862
|
+
const internalGrant = {
|
|
2863
|
+
...grant,
|
|
2864
|
+
to: toArray(grant.to),
|
|
2865
|
+
tables: [tableTarget]
|
|
2866
|
+
};
|
|
2867
|
+
effectiveGrants.push(internalGrant);
|
|
2868
|
+
}
|
|
2869
|
+
}
|
|
2870
|
+
return effectiveGrants.length ? effectiveGrants : void 0;
|
|
2871
|
+
};
|
|
2219
2872
|
const compareDbStructures = (a, b, i, path) => {
|
|
2220
2873
|
let err;
|
|
2221
2874
|
if (typeof a !== typeof b) err = true;
|
|
@@ -2251,7 +2904,10 @@ const getActualItems = async (db, currentSchema, internal, columnTypes) => {
|
|
|
2251
2904
|
codeItems.tables.push({
|
|
2252
2905
|
table: table.table,
|
|
2253
2906
|
shape: table.shape,
|
|
2254
|
-
internal:
|
|
2907
|
+
internal: {
|
|
2908
|
+
...table.internal,
|
|
2909
|
+
rls: internal.rls
|
|
2910
|
+
},
|
|
2255
2911
|
q: { schema: getQuerySchema(table) }
|
|
2256
2912
|
});
|
|
2257
2913
|
for (const key in table.relations) {
|
|
@@ -2296,8 +2952,27 @@ const getActualItems = async (db, currentSchema, internal, columnTypes) => {
|
|
|
2296
2952
|
for (const privilege of role.defaultPrivileges) if (privilege.schema) codeItems.schemas.add(privilege.schema);
|
|
2297
2953
|
}
|
|
2298
2954
|
}
|
|
2955
|
+
if (internal.grants) for (const grant of internal.grants) addGrantSchemas(codeItems.schemas, currentSchema, grant);
|
|
2299
2956
|
return codeItems;
|
|
2300
2957
|
};
|
|
2958
|
+
const addGrantSchemas = (schemas, currentSchema, grant) => {
|
|
2959
|
+
for (const schema of [
|
|
2960
|
+
...grant.schemas ?? [],
|
|
2961
|
+
...grant.allTablesIn ?? [],
|
|
2962
|
+
...grant.allSequencesIn ?? [],
|
|
2963
|
+
...grant.allRoutinesIn ?? []
|
|
2964
|
+
]) schemas.add(schema);
|
|
2965
|
+
for (const target of [
|
|
2966
|
+
...grant.tables ?? [],
|
|
2967
|
+
...grant.sequences ?? [],
|
|
2968
|
+
...grant.routines ?? [],
|
|
2969
|
+
...grant.types ?? [],
|
|
2970
|
+
...grant.domains ?? []
|
|
2971
|
+
]) {
|
|
2972
|
+
const [schema] = getSchemaAndTableFromName(currentSchema, target);
|
|
2973
|
+
if (schema) schemas.add(schema);
|
|
2974
|
+
}
|
|
2975
|
+
};
|
|
2301
2976
|
const processEnumColumn = (column, currentSchema, codeItems) => {
|
|
2302
2977
|
const { enumName, options } = column;
|
|
2303
2978
|
const [schema, name] = getSchemaAndTableFromName(currentSchema, enumName);
|