rake-db 2.34.1 → 2.35.0
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 +98 -2
- package/dist/index.js +573 -13
- package/dist/index.js.map +1 -1
- package/dist/index.mjs +574 -14
- package/dist/index.mjs.map +1 -1
- package/package.json +3 -3
package/dist/index.mjs
CHANGED
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import { ArrayColumn, Column, CustomTypeColumn, DomainColumn, EnumColumn, PostgisGeographyPointColumn, RawSql, TimestampColumn, TimestampTZColumn, UnknownColumn, addCode, assignDbDataToColumn, backtickQuote, codeToString, colors, constraintInnerToCode, consumeColumnName, deepCompare, defaultSchemaConfig, emptyArray, emptyObject, escapeForMigration, escapeString, excludeInnerToCode, exhaustive, getColumnTypes, getImportPath, getStackTrace, indexInnerToCode, isRawSQL, logParamToLogObject, makeColumnTypes, makeColumnsByType, parseTableData, parseTableDataInput, pathToLog, primaryKeyInnerToCode, pushTableDataCode, quoteObjectKey, raw, rawSqlToCode, referencesArgsToCode, setCurrentColumnName, setDefaultLanguage, singleQuote, tableDataMethods, toArray, toCamelCase, toSnakeCase } from "pqb/internal";
|
|
1
|
+
import { ArrayColumn, Column, CustomTypeColumn, DomainColumn, EnumColumn, PostgisGeographyPointColumn, RawSql, TimestampColumn, TimestampTZColumn, UnknownColumn, addCode, assignDbDataToColumn, backtickQuote, codeToString, colors, constraintInnerToCode, consumeColumnName, deepCompare, defaultSchemaConfig, emptyArray, emptyObject, escapeForMigration, escapeString, excludeInnerToCode, exhaustive, getColumnTypes, getImportPath, getStackTrace, indexInnerToCode, isRawSQL, logParamToLogObject, makeColumnTypes, makeColumnsByType, parseTableData, parseTableDataInput, pathToLog, primaryKeyInnerToCode, pushTableDataCode, quoteIdentifier, quoteObjectKey, raw, rawSqlToCode, referencesArgsToCode, setCurrentColumnName, setDefaultLanguage, singleQuote, tableDataMethods, toArray, toCamelCase, toSnakeCase } from "pqb/internal";
|
|
2
2
|
import path, { join } from "node:path";
|
|
3
3
|
import { fileURLToPath, pathToFileURL } from "node:url";
|
|
4
4
|
import { createDbWithAdapter } from "pqb";
|
|
@@ -1143,13 +1143,12 @@ const astToQuery$1 = (ast) => {
|
|
|
1143
1143
|
if (options?.recursive) sql.push("RECURSIVE");
|
|
1144
1144
|
sql.push(`VIEW ${sqlName}`);
|
|
1145
1145
|
if (options?.columns) sql.push(`(${options.columns.map((column) => `"${column}"`).join(", ")})`);
|
|
1146
|
-
|
|
1147
|
-
|
|
1148
|
-
|
|
1149
|
-
|
|
1150
|
-
|
|
1151
|
-
|
|
1152
|
-
}
|
|
1146
|
+
const withOptions = options?.with;
|
|
1147
|
+
const list = [];
|
|
1148
|
+
if (withOptions?.checkOption) list.push(`check_option = ${singleQuote(withOptions.checkOption)}`);
|
|
1149
|
+
if (withOptions?.securityBarrier) list.push(`security_barrier = true`);
|
|
1150
|
+
if (withOptions?.securityInvoker !== false) list.push(`security_invoker = true`);
|
|
1151
|
+
if (list.length) sql.push(`WITH ( ${list.join(", ")} )`);
|
|
1153
1152
|
sql.push(`AS (${ast.sql.toSQL({ values })})`);
|
|
1154
1153
|
} else {
|
|
1155
1154
|
sql.push("DROP VIEW");
|
|
@@ -1350,12 +1349,12 @@ const objectConfigToSql = (ast, config, action) => {
|
|
|
1350
1349
|
const objectType = key;
|
|
1351
1350
|
if (!value) continue;
|
|
1352
1351
|
const { privileges, grantablePrivileges } = value;
|
|
1353
|
-
if (privileges?.length) queries.push(buildQuery(ast, objectType, privileges, false, action));
|
|
1354
|
-
if (grantablePrivileges?.length) queries.push(buildQuery(ast, objectType, grantablePrivileges, true, action));
|
|
1352
|
+
if (privileges?.length) queries.push(buildQuery$1(ast, objectType, privileges, false, action));
|
|
1353
|
+
if (grantablePrivileges?.length) queries.push(buildQuery$1(ast, objectType, grantablePrivileges, true, action));
|
|
1355
1354
|
}
|
|
1356
1355
|
return queries;
|
|
1357
1356
|
};
|
|
1358
|
-
const buildQuery = (ast, objectType, privileges, grantable, action) => {
|
|
1357
|
+
const buildQuery$1 = (ast, objectType, privileges, grantable, action) => {
|
|
1359
1358
|
const parts = ["ALTER DEFAULT PRIVILEGES"];
|
|
1360
1359
|
if (ast.owner) parts.push(`FOR ROLE "${ast.owner}"`);
|
|
1361
1360
|
if (ast.schema) parts.push(`IN SCHEMA "${ast.schema}"`);
|
|
@@ -1382,6 +1381,204 @@ const enableOrDisableRls = (migration, up, tableName) => {
|
|
|
1382
1381
|
const forceOrNoForceRls = (migration, up, tableName) => {
|
|
1383
1382
|
return setRls(migration, tableName, up ? "force" : "noForce");
|
|
1384
1383
|
};
|
|
1384
|
+
const quotedRoles = (roles) => {
|
|
1385
|
+
if (!roles) return;
|
|
1386
|
+
const arr = Array.isArray(roles) ? roles : [roles];
|
|
1387
|
+
if (!arr.length) return;
|
|
1388
|
+
return arr.map(quoteIdentifier).join(", ");
|
|
1389
|
+
};
|
|
1390
|
+
const normalizeRoles = (roles) => {
|
|
1391
|
+
if (!roles) return;
|
|
1392
|
+
return Array.isArray(roles) ? roles : [roles];
|
|
1393
|
+
};
|
|
1394
|
+
const rolesEqual = (a, b) => {
|
|
1395
|
+
const left = normalizeRoles(a);
|
|
1396
|
+
const right = normalizeRoles(b);
|
|
1397
|
+
return JSON.stringify(left) === JSON.stringify(right);
|
|
1398
|
+
};
|
|
1399
|
+
const rawSql = (_migration, sql, values) => sql.toSQL({ values });
|
|
1400
|
+
const createPolicySql = (migration, tableName, policyName, params) => {
|
|
1401
|
+
const [schema, table] = getSchemaAndTableFromName(migration.adapter.getSchema(), tableName);
|
|
1402
|
+
const values = [];
|
|
1403
|
+
const rolesSql = quotedRoles(params.to);
|
|
1404
|
+
let usingSql;
|
|
1405
|
+
let withCheckSql;
|
|
1406
|
+
if (params.for === "SELECT" || params.for === "DELETE") usingSql = rawSql(migration, params.using, values);
|
|
1407
|
+
else if (params.for === "INSERT") withCheckSql = rawSql(migration, params.withCheck, values);
|
|
1408
|
+
else {
|
|
1409
|
+
if (!params.withCheck) throw new Error("WITH CHECK is required for ALL and UPDATE policies");
|
|
1410
|
+
usingSql = rawSql(migration, params.using, values);
|
|
1411
|
+
withCheckSql = rawSql(migration, params.withCheck, values);
|
|
1412
|
+
}
|
|
1413
|
+
return {
|
|
1414
|
+
text: `CREATE POLICY ${quoteIdentifier(policyName)}
|
|
1415
|
+
ON ${quoteTable(schema, table)}
|
|
1416
|
+
AS ${params.as}
|
|
1417
|
+
FOR ${params.for ?? "ALL"}${rolesSql ? `
|
|
1418
|
+
TO ${rolesSql}` : ""}${usingSql ? `
|
|
1419
|
+
USING (${usingSql})` : ""}${withCheckSql ? `
|
|
1420
|
+
WITH CHECK (${withCheckSql})` : ""}`,
|
|
1421
|
+
values
|
|
1422
|
+
};
|
|
1423
|
+
};
|
|
1424
|
+
const dropPolicySql = (migration, tableName, policyName) => {
|
|
1425
|
+
const [schema, table] = getSchemaAndTableFromName(migration.adapter.getSchema(), tableName);
|
|
1426
|
+
return `DROP POLICY ${quoteIdentifier(policyName)} ON ${quoteTable(schema, table)}`;
|
|
1427
|
+
};
|
|
1428
|
+
const createOrDropPolicy = async (migration, up, tableName, policyName, params) => {
|
|
1429
|
+
if (up) {
|
|
1430
|
+
const { text, values } = createPolicySql(migration, tableName, policyName, params);
|
|
1431
|
+
await migration.adapter.arrays(text, values);
|
|
1432
|
+
} else await migration.adapter.arrays(dropPolicySql(migration, tableName, policyName));
|
|
1433
|
+
};
|
|
1434
|
+
const isRecreateDefinition = (value) => {
|
|
1435
|
+
return "as" in value || "for" in value || "table" in value;
|
|
1436
|
+
};
|
|
1437
|
+
const changePolicyInPlace = async (migration, tableName, policyName, from, to) => {
|
|
1438
|
+
const [schema, table] = getSchemaAndTableFromName(migration.adapter.getSchema(), tableName);
|
|
1439
|
+
const quotedTable = quoteTable(schema, table);
|
|
1440
|
+
let currentName = from.name ?? policyName;
|
|
1441
|
+
const targetName = to.name ?? policyName;
|
|
1442
|
+
if (currentName !== targetName) {
|
|
1443
|
+
await migration.adapter.arrays(`ALTER POLICY ${quoteIdentifier(currentName)}
|
|
1444
|
+
ON ${quotedTable}
|
|
1445
|
+
RENAME TO ${quoteIdentifier(targetName)}`);
|
|
1446
|
+
currentName = targetName;
|
|
1447
|
+
}
|
|
1448
|
+
if ("to" in from && "to" in to && !rolesEqual(from.to, to.to)) await migration.adapter.arrays(`ALTER POLICY ${quoteIdentifier(currentName)}
|
|
1449
|
+
ON ${quotedTable}
|
|
1450
|
+
TO ${quotedRoles(to.to) ?? "PUBLIC"}`);
|
|
1451
|
+
if ("using" in from && "using" in to && from.using && to.using) {
|
|
1452
|
+
const fromUsing = rawSql(migration, from.using, []);
|
|
1453
|
+
const toUsingValues = [];
|
|
1454
|
+
const toUsing = rawSql(migration, to.using, toUsingValues);
|
|
1455
|
+
if (fromUsing !== toUsing) await migration.adapter.arrays(`ALTER POLICY ${quoteIdentifier(currentName)}
|
|
1456
|
+
ON ${quotedTable}
|
|
1457
|
+
USING (${toUsing})`, toUsingValues);
|
|
1458
|
+
}
|
|
1459
|
+
if ("withCheck" in from && "withCheck" in to && from.withCheck && to.withCheck) {
|
|
1460
|
+
const fromWithCheck = rawSql(migration, from.withCheck, []);
|
|
1461
|
+
const toWithCheckValues = [];
|
|
1462
|
+
const toWithCheck = rawSql(migration, to.withCheck, toWithCheckValues);
|
|
1463
|
+
if (fromWithCheck !== toWithCheck) await migration.adapter.arrays(`ALTER POLICY ${quoteIdentifier(currentName)}
|
|
1464
|
+
ON ${quotedTable}
|
|
1465
|
+
WITH CHECK (${toWithCheck})`, toWithCheckValues);
|
|
1466
|
+
}
|
|
1467
|
+
};
|
|
1468
|
+
const recreatePolicy = async (migration, tableName, policyName, from, to) => {
|
|
1469
|
+
const fromTable = from.table ?? tableName;
|
|
1470
|
+
const fromName = from.name ?? policyName;
|
|
1471
|
+
await migration.adapter.arrays(dropPolicySql(migration, fromTable, fromName));
|
|
1472
|
+
const { text, values } = createPolicySql(migration, to.table ?? tableName, to.name ?? policyName, to);
|
|
1473
|
+
await migration.adapter.arrays(text, values);
|
|
1474
|
+
};
|
|
1475
|
+
const changePolicy = async (migration, up, tableName, policyName, params) => {
|
|
1476
|
+
const from = up ? params.from : params.to;
|
|
1477
|
+
const to = up ? params.to : params.from;
|
|
1478
|
+
if (isRecreateDefinition(from) || isRecreateDefinition(to)) await recreatePolicy(migration, tableName, policyName, from, to);
|
|
1479
|
+
else await changePolicyInPlace(migration, tableName, policyName, from, to);
|
|
1480
|
+
};
|
|
1481
|
+
const dropOrCreatePolicy = (migration, up, tableName, policyName, params) => {
|
|
1482
|
+
return createOrDropPolicy(migration, !up, tableName, policyName, params);
|
|
1483
|
+
};
|
|
1484
|
+
const concreteTargetKeyToSql = {
|
|
1485
|
+
tables: "TABLE",
|
|
1486
|
+
sequences: "SEQUENCE",
|
|
1487
|
+
routines: "ROUTINE",
|
|
1488
|
+
types: "TYPE",
|
|
1489
|
+
domains: "DOMAIN"
|
|
1490
|
+
};
|
|
1491
|
+
const schemaWideTargetKeyToSql = {
|
|
1492
|
+
allTablesIn: "ALL TABLES IN SCHEMA",
|
|
1493
|
+
allSequencesIn: "ALL SEQUENCES IN SCHEMA",
|
|
1494
|
+
allRoutinesIn: "ALL ROUTINES IN SCHEMA"
|
|
1495
|
+
};
|
|
1496
|
+
const schemaOrDatabaseTargetKeyToSql = {
|
|
1497
|
+
schemas: "SCHEMA",
|
|
1498
|
+
databases: "DATABASE"
|
|
1499
|
+
};
|
|
1500
|
+
const specialRoleSpecs = new Set([
|
|
1501
|
+
"PUBLIC",
|
|
1502
|
+
"CURRENT_ROLE",
|
|
1503
|
+
"CURRENT_USER",
|
|
1504
|
+
"SESSION_USER"
|
|
1505
|
+
]);
|
|
1506
|
+
const changeGrant = async (migration, up, params) => {
|
|
1507
|
+
const sql = privilegeToSql(migration, {
|
|
1508
|
+
...params,
|
|
1509
|
+
to: typeof params.to === "string" ? [params.to] : params.to,
|
|
1510
|
+
action: up ? "grant" : "revoke"
|
|
1511
|
+
});
|
|
1512
|
+
if (sql.length) await migration.adapter.arrays(sql.join(";\n"));
|
|
1513
|
+
};
|
|
1514
|
+
const privilegeToSql = (migration, ast) => {
|
|
1515
|
+
const queries = [];
|
|
1516
|
+
const isRevoke = ast.action === "revoke";
|
|
1517
|
+
const currentSchema = migration.adapter.getSchema();
|
|
1518
|
+
for (const key of [
|
|
1519
|
+
"tables",
|
|
1520
|
+
"sequences",
|
|
1521
|
+
"routines",
|
|
1522
|
+
"types",
|
|
1523
|
+
"domains"
|
|
1524
|
+
]) {
|
|
1525
|
+
const targetNames = ast[key];
|
|
1526
|
+
if (!targetNames?.length) continue;
|
|
1527
|
+
const privileges = ast.privileges;
|
|
1528
|
+
const grantablePrivileges = ast.grantablePrivileges;
|
|
1529
|
+
const quotedTargets = targetNames.map((name) => {
|
|
1530
|
+
const [schema, objName] = getSchemaAndTableFromName(currentSchema, name);
|
|
1531
|
+
if (schema) return `"${schema}"."${objName}"`;
|
|
1532
|
+
return `"${objName}"`;
|
|
1533
|
+
});
|
|
1534
|
+
addTargetQueries(queries, ast, `ON ${concreteTargetKeyToSql[key]} ${quotedTargets.join(", ")}`, privileges, grantablePrivileges, isRevoke);
|
|
1535
|
+
}
|
|
1536
|
+
for (const key of [
|
|
1537
|
+
"allTablesIn",
|
|
1538
|
+
"allSequencesIn",
|
|
1539
|
+
"allRoutinesIn"
|
|
1540
|
+
]) {
|
|
1541
|
+
const targetNames = ast[key];
|
|
1542
|
+
if (!targetNames?.length) continue;
|
|
1543
|
+
const targetList = targetNames.map((name) => `"${name}"`);
|
|
1544
|
+
const privileges = ast.privileges;
|
|
1545
|
+
const grantablePrivileges = ast.grantablePrivileges;
|
|
1546
|
+
addTargetQueries(queries, ast, `ON ${schemaWideTargetKeyToSql[key]} ${targetList.join(", ")}`, privileges, grantablePrivileges, isRevoke);
|
|
1547
|
+
}
|
|
1548
|
+
for (const key of ["schemas", "databases"]) {
|
|
1549
|
+
const targetNames = ast[key];
|
|
1550
|
+
if (!targetNames?.length) continue;
|
|
1551
|
+
const targetList = targetNames.map((name) => `"${name}"`);
|
|
1552
|
+
const privileges = ast.privileges;
|
|
1553
|
+
const grantablePrivileges = ast.grantablePrivileges;
|
|
1554
|
+
addTargetQueries(queries, ast, `ON ${schemaOrDatabaseTargetKeyToSql[key]} ${targetList.join(", ")}`, privileges, grantablePrivileges, isRevoke);
|
|
1555
|
+
}
|
|
1556
|
+
return queries;
|
|
1557
|
+
};
|
|
1558
|
+
const addTargetQueries = (queries, ast, targetSql, privileges, grantablePrivileges, isRevoke) => {
|
|
1559
|
+
if (privileges?.length) queries.push(buildQuery(ast, privileges, targetSql, false, isRevoke));
|
|
1560
|
+
if (grantablePrivileges?.length) queries.push(buildQuery(ast, grantablePrivileges, targetSql, !isRevoke, isRevoke));
|
|
1561
|
+
};
|
|
1562
|
+
const buildQuery = (ast, privileges, targetSql, grantable, isRevoke) => {
|
|
1563
|
+
const parts = [];
|
|
1564
|
+
if (isRevoke) {
|
|
1565
|
+
parts.push("REVOKE");
|
|
1566
|
+
if (grantable) parts.push("GRANT OPTION FOR");
|
|
1567
|
+
} else parts.push("GRANT");
|
|
1568
|
+
const privilegeList = privileges.map((p) => p === "ALL" ? "ALL PRIVILEGES" : p === "TEMP" ? "TEMPORARY" : p).join(", ");
|
|
1569
|
+
parts.push(privilegeList);
|
|
1570
|
+
parts.push(targetSql);
|
|
1571
|
+
if (isRevoke) parts.push("FROM");
|
|
1572
|
+
else parts.push("TO");
|
|
1573
|
+
parts.push(ast.to.map(formatRoleSpec).join(", "));
|
|
1574
|
+
if (ast.grantedBy) parts.push("GRANTED BY", `"${ast.grantedBy}"`);
|
|
1575
|
+
if (isRevoke && ast.revokeMode) parts.push(ast.revokeMode);
|
|
1576
|
+
if (!isRevoke && grantable) parts.push("WITH GRANT OPTION");
|
|
1577
|
+
return parts.join(" ");
|
|
1578
|
+
};
|
|
1579
|
+
const formatRoleSpec = (role) => {
|
|
1580
|
+
return specialRoleSpecs.has(role) ? role : `"${role}"`;
|
|
1581
|
+
};
|
|
1385
1582
|
/**
|
|
1386
1583
|
* Creates a new `db` instance that is an instance of `pqb` with mixed in migration methods from the `Migration` class.
|
|
1387
1584
|
* It overrides `query` and `array` db adapter methods to intercept SQL for the logging.
|
|
@@ -2255,6 +2452,21 @@ var Migration = class {
|
|
|
2255
2452
|
noForceRls(tableName) {
|
|
2256
2453
|
return forceOrNoForceRls(this, !this.up, tableName);
|
|
2257
2454
|
}
|
|
2455
|
+
createPolicy(tableName, policyName, params) {
|
|
2456
|
+
return createOrDropPolicy(this, this.up, tableName, policyName, params);
|
|
2457
|
+
}
|
|
2458
|
+
dropPolicy(tableName, policyName, params) {
|
|
2459
|
+
return dropOrCreatePolicy(this, this.up, tableName, policyName, params);
|
|
2460
|
+
}
|
|
2461
|
+
changePolicy(tableName, policyName, params) {
|
|
2462
|
+
return changePolicy(this, this.up, tableName, policyName, params);
|
|
2463
|
+
}
|
|
2464
|
+
grant(params) {
|
|
2465
|
+
return changeGrant(this, this.up, params);
|
|
2466
|
+
}
|
|
2467
|
+
revoke(params) {
|
|
2468
|
+
return changeGrant(this, !this.up, params);
|
|
2469
|
+
}
|
|
2258
2470
|
};
|
|
2259
2471
|
const wrapWithEnhancingError = async (text, values, promise) => {
|
|
2260
2472
|
try {
|
|
@@ -3599,6 +3811,38 @@ const astToGenerateItem = (config, ast, currentSchema) => {
|
|
|
3599
3811
|
deps.push(schema, `${schema}.${ast.table}`);
|
|
3600
3812
|
break;
|
|
3601
3813
|
}
|
|
3814
|
+
case "policy": {
|
|
3815
|
+
const defaultSchema = ast.schema ?? currentSchema;
|
|
3816
|
+
const [tableSchema = defaultSchema, tableName] = getSchemaAndTableFromName(defaultSchema, ast.table);
|
|
3817
|
+
deps.push(tableSchema, `${tableSchema}.${tableName}`);
|
|
3818
|
+
pushPolicyRoleDeps(deps, ast.to);
|
|
3819
|
+
if (ast.action === "create") add.push(`policy:${ast.name}`);
|
|
3820
|
+
else drop.push(`policy:${ast.name}`);
|
|
3821
|
+
break;
|
|
3822
|
+
}
|
|
3823
|
+
case "changePolicy": {
|
|
3824
|
+
const defaultSchema = ast.schema ?? currentSchema;
|
|
3825
|
+
const [tableSchema = defaultSchema, tableName] = getSchemaAndTableFromName(defaultSchema, ast.table);
|
|
3826
|
+
deps.push(tableSchema, `${tableSchema}.${tableName}`);
|
|
3827
|
+
const fromTable = ast.from.table ? ast.from.table : tableName;
|
|
3828
|
+
const fromName = ast.from.name ? ast.from.name : ast.name;
|
|
3829
|
+
const toTable = ast.to.table ? ast.to.table : tableName;
|
|
3830
|
+
const toName = ast.to.name ? ast.to.name : ast.name;
|
|
3831
|
+
const [fromSchema = tableSchema, fromTableName] = getSchemaAndTableFromName(tableSchema, fromTable);
|
|
3832
|
+
const [toSchema = tableSchema, toTableName] = getSchemaAndTableFromName(tableSchema, toTable);
|
|
3833
|
+
deps.push(fromSchema, `${fromSchema}.${fromTableName}`);
|
|
3834
|
+
deps.push(toSchema, `${toSchema}.${toTableName}`);
|
|
3835
|
+
if (fromName !== toName) {
|
|
3836
|
+
drop.push(`policy:${fromName}`);
|
|
3837
|
+
add.push(`policy:${toName}`);
|
|
3838
|
+
}
|
|
3839
|
+
pushPolicyRoleDeps(deps, ast.from.to);
|
|
3840
|
+
pushPolicyRoleDeps(deps, ast.to.to);
|
|
3841
|
+
break;
|
|
3842
|
+
}
|
|
3843
|
+
case "grant":
|
|
3844
|
+
pushGrantDeps(currentSchema, deps, ast);
|
|
3845
|
+
break;
|
|
3602
3846
|
default: exhaustive(ast);
|
|
3603
3847
|
}
|
|
3604
3848
|
return {
|
|
@@ -3662,6 +3906,39 @@ const analyzeTableData = (config, currentSchema, schema, table, keys, deps, data
|
|
|
3662
3906
|
}
|
|
3663
3907
|
}
|
|
3664
3908
|
};
|
|
3909
|
+
const pushPolicyRoleDeps = (deps, roles) => {
|
|
3910
|
+
if (!roles) return;
|
|
3911
|
+
deps.push(...roles.map((role) => `role:${role}`));
|
|
3912
|
+
};
|
|
3913
|
+
const grantConcreteTargetKeys = [
|
|
3914
|
+
"tables",
|
|
3915
|
+
"sequences",
|
|
3916
|
+
"routines",
|
|
3917
|
+
"types",
|
|
3918
|
+
"domains"
|
|
3919
|
+
];
|
|
3920
|
+
const grantSchemaWideTargetKeys = [
|
|
3921
|
+
"allTablesIn",
|
|
3922
|
+
"allSequencesIn",
|
|
3923
|
+
"allRoutinesIn"
|
|
3924
|
+
];
|
|
3925
|
+
const pushGrantDeps = (currentSchema, deps, ast) => {
|
|
3926
|
+
deps.push(...ast.to.map((role) => `role:${role}`));
|
|
3927
|
+
if (ast.grantedBy) deps.push(`role:${ast.grantedBy}`);
|
|
3928
|
+
if (ast.schemas) deps.push(...ast.schemas);
|
|
3929
|
+
for (const key of grantSchemaWideTargetKeys) {
|
|
3930
|
+
const schemas = ast[key];
|
|
3931
|
+
if (schemas) deps.push(...schemas);
|
|
3932
|
+
}
|
|
3933
|
+
for (const key of grantConcreteTargetKeys) {
|
|
3934
|
+
const targets = ast[key];
|
|
3935
|
+
if (!targets) continue;
|
|
3936
|
+
for (const target of targets) {
|
|
3937
|
+
const [schema = currentSchema, name] = getSchemaAndTableFromName(currentSchema, target);
|
|
3938
|
+
deps.push(schema, `${schema}.${name}`);
|
|
3939
|
+
}
|
|
3940
|
+
}
|
|
3941
|
+
};
|
|
3665
3942
|
const astToMigration = (currentSchema, config, asts) => {
|
|
3666
3943
|
const items = astToGenerateItems(config, asts, currentSchema);
|
|
3667
3944
|
const toBeAdded = /* @__PURE__ */ new Set();
|
|
@@ -4071,8 +4348,91 @@ const astEncoders = {
|
|
|
4071
4348
|
name: ast.table
|
|
4072
4349
|
}, currentSchema);
|
|
4073
4350
|
return `await db.${ast.action === "enable" ? "enableRls" : ast.action === "disable" ? "disableRls" : ast.action === "force" ? "forceRls" : "noForceRls"}(${table});`;
|
|
4351
|
+
},
|
|
4352
|
+
policy(ast, _config, currentSchema) {
|
|
4353
|
+
const table = quoteSchemaTable({
|
|
4354
|
+
schema: ast.schema,
|
|
4355
|
+
name: ast.table
|
|
4356
|
+
}, currentSchema);
|
|
4357
|
+
return [
|
|
4358
|
+
`await db.${ast.action}Policy(${table}, ${singleQuote(ast.name)}, {`,
|
|
4359
|
+
policyDefinitionToCode(ast),
|
|
4360
|
+
"});"
|
|
4361
|
+
];
|
|
4362
|
+
},
|
|
4363
|
+
changePolicy(ast, _config, currentSchema) {
|
|
4364
|
+
return [
|
|
4365
|
+
`await db.changePolicy(${quoteSchemaTable({
|
|
4366
|
+
schema: ast.schema,
|
|
4367
|
+
name: ast.table
|
|
4368
|
+
}, currentSchema)}, ${singleQuote(ast.name)}, {`,
|
|
4369
|
+
[
|
|
4370
|
+
"from: {",
|
|
4371
|
+
policyChangeDefinitionToCode(ast.from),
|
|
4372
|
+
"},",
|
|
4373
|
+
"to: {",
|
|
4374
|
+
policyChangeDefinitionToCode(ast.to),
|
|
4375
|
+
"},"
|
|
4376
|
+
],
|
|
4377
|
+
"});"
|
|
4378
|
+
];
|
|
4379
|
+
},
|
|
4380
|
+
grant(ast) {
|
|
4381
|
+
const props = [];
|
|
4382
|
+
props.push(`to: [${ast.to.map(singleQuote).join(", ")}],`);
|
|
4383
|
+
for (const key of grantTargetKeys) {
|
|
4384
|
+
const values = ast[key];
|
|
4385
|
+
if (values?.length) props.push(`${key}: [${values.map(singleQuote).join(", ")}],`);
|
|
4386
|
+
}
|
|
4387
|
+
if (ast.privileges?.length) props.push(`privileges: [${ast.privileges.map(singleQuote).join(", ")}],`);
|
|
4388
|
+
if (ast.grantablePrivileges?.length) props.push(`grantablePrivileges: [${ast.grantablePrivileges.map(singleQuote).join(", ")}],`);
|
|
4389
|
+
if (ast.grantedBy) props.push(`grantedBy: ${singleQuote(ast.grantedBy)},`);
|
|
4390
|
+
if (ast.revokeMode) props.push(`revokeMode: ${singleQuote(ast.revokeMode)},`);
|
|
4391
|
+
return [
|
|
4392
|
+
`await db.${ast.action}({`,
|
|
4393
|
+
props,
|
|
4394
|
+
"});"
|
|
4395
|
+
];
|
|
4074
4396
|
}
|
|
4075
4397
|
};
|
|
4398
|
+
const grantTargetKeys = [
|
|
4399
|
+
"schemas",
|
|
4400
|
+
"tables",
|
|
4401
|
+
"sequences",
|
|
4402
|
+
"routines",
|
|
4403
|
+
"types",
|
|
4404
|
+
"domains",
|
|
4405
|
+
"databases",
|
|
4406
|
+
"allTablesIn",
|
|
4407
|
+
"allSequencesIn",
|
|
4408
|
+
"allRoutinesIn"
|
|
4409
|
+
];
|
|
4410
|
+
const policyDefinitionToCode = (policy) => {
|
|
4411
|
+
const code = [`as: ${singleQuote(policy.as)},`];
|
|
4412
|
+
if (policy.for) code.push(`for: ${singleQuote(policy.for)},`);
|
|
4413
|
+
if (policy.to?.length) code.push(`to: [${policy.to.map(singleQuote).join(", ")}],`);
|
|
4414
|
+
if (policy.using) code.push(`using: ${rawSqlStringToCode(policy.using)},`);
|
|
4415
|
+
if (policy.withCheck) code.push(`withCheck: ${rawSqlStringToCode(policy.withCheck)},`);
|
|
4416
|
+
return code;
|
|
4417
|
+
};
|
|
4418
|
+
const policyChangeDefinitionToCode = (policy) => {
|
|
4419
|
+
const code = [];
|
|
4420
|
+
if ("table" in policy && policy.table) code.push(`table: ${singleQuote(policy.table)},`);
|
|
4421
|
+
if (policy.name) code.push(`name: ${singleQuote(policy.name)},`);
|
|
4422
|
+
if (isPolicyRecreateDefinition(policy)) code.push(...policyDefinitionToCode(policy));
|
|
4423
|
+
else {
|
|
4424
|
+
if (policy.to?.length) code.push(`to: [${policy.to.map(singleQuote).join(", ")}],`);
|
|
4425
|
+
if (policy.using) code.push(`using: ${rawSqlStringToCode(policy.using)},`);
|
|
4426
|
+
if (policy.withCheck) code.push(`withCheck: ${rawSqlStringToCode(policy.withCheck)},`);
|
|
4427
|
+
}
|
|
4428
|
+
return code;
|
|
4429
|
+
};
|
|
4430
|
+
const isPolicyRecreateDefinition = (policy) => {
|
|
4431
|
+
return policy.as !== void 0 || policy.for !== void 0 || policy.table !== void 0;
|
|
4432
|
+
};
|
|
4433
|
+
const rawSqlStringToCode = (sql) => {
|
|
4434
|
+
return `db.sql${backtickQuote(sql)}`;
|
|
4435
|
+
};
|
|
4076
4436
|
const roleParams = (name, ast, compare, to) => {
|
|
4077
4437
|
const params = [];
|
|
4078
4438
|
for (const key of [
|
|
@@ -4534,7 +4894,154 @@ const defaultPrivilegesSql = `SELECT COALESCE(json_agg(t.*), '[]') FROM (
|
|
|
4534
4894
|
JOIN LATERAL aclexplode(d.defaclacl) ae ON true
|
|
4535
4895
|
GROUP BY "grantor", "grantee", "schema", "object"
|
|
4536
4896
|
) t`;
|
|
4537
|
-
const
|
|
4897
|
+
const grantsSql = `SELECT COALESCE(json_agg(t.* ORDER BY t."target", t."schema", t."name", t."grantee", t."grantor"), '[]') FROM (
|
|
4898
|
+
SELECT
|
|
4899
|
+
(ae.grantor)::regrole "grantor",
|
|
4900
|
+
CASE
|
|
4901
|
+
WHEN ae.grantee = 0 THEN 'PUBLIC'
|
|
4902
|
+
ELSE (ae.grantee)::regrole::text
|
|
4903
|
+
END "grantee",
|
|
4904
|
+
n.nspname "schema",
|
|
4905
|
+
c.relname "name",
|
|
4906
|
+
'tables' "target",
|
|
4907
|
+
array_agg(ae.privilege_type ORDER BY ae.privilege_type) "privileges",
|
|
4908
|
+
array_agg(ae.is_grantable ORDER BY ae.privilege_type) "isGrantables"
|
|
4909
|
+
FROM pg_class c
|
|
4910
|
+
JOIN pg_namespace n ON n.oid = c.relnamespace
|
|
4911
|
+
JOIN LATERAL aclexplode(coalesce(c.relacl, acldefault('r', c.relowner))) ae ON true
|
|
4912
|
+
WHERE c.relkind IN ('r', 'p')
|
|
4913
|
+
AND ${filterSchema("n.nspname")}
|
|
4914
|
+
AND ae.grantee <> c.relowner
|
|
4915
|
+
GROUP BY "grantor", "grantee", "schema", "name", "target"
|
|
4916
|
+
|
|
4917
|
+
UNION ALL
|
|
4918
|
+
|
|
4919
|
+
SELECT
|
|
4920
|
+
(ae.grantor)::regrole "grantor",
|
|
4921
|
+
CASE
|
|
4922
|
+
WHEN ae.grantee = 0 THEN 'PUBLIC'
|
|
4923
|
+
ELSE (ae.grantee)::regrole::text
|
|
4924
|
+
END "grantee",
|
|
4925
|
+
n.nspname "schema",
|
|
4926
|
+
c.relname "name",
|
|
4927
|
+
'sequences' "target",
|
|
4928
|
+
array_agg(ae.privilege_type ORDER BY ae.privilege_type) "privileges",
|
|
4929
|
+
array_agg(ae.is_grantable ORDER BY ae.privilege_type) "isGrantables"
|
|
4930
|
+
FROM pg_class c
|
|
4931
|
+
JOIN pg_namespace n ON n.oid = c.relnamespace
|
|
4932
|
+
JOIN LATERAL aclexplode(coalesce(c.relacl, acldefault('S', c.relowner))) ae ON true
|
|
4933
|
+
WHERE c.relkind = 'S'
|
|
4934
|
+
AND ${filterSchema("n.nspname")}
|
|
4935
|
+
AND ae.grantee <> c.relowner
|
|
4936
|
+
GROUP BY "grantor", "grantee", "schema", "name", "target"
|
|
4937
|
+
|
|
4938
|
+
UNION ALL
|
|
4939
|
+
|
|
4940
|
+
SELECT
|
|
4941
|
+
(ae.grantor)::regrole "grantor",
|
|
4942
|
+
CASE
|
|
4943
|
+
WHEN ae.grantee = 0 THEN 'PUBLIC'
|
|
4944
|
+
ELSE (ae.grantee)::regrole::text
|
|
4945
|
+
END "grantee",
|
|
4946
|
+
n.nspname "schema",
|
|
4947
|
+
p.proname "name",
|
|
4948
|
+
'routines' "target",
|
|
4949
|
+
array_agg(ae.privilege_type ORDER BY ae.privilege_type) "privileges",
|
|
4950
|
+
array_agg(ae.is_grantable ORDER BY ae.privilege_type) "isGrantables"
|
|
4951
|
+
FROM pg_proc p
|
|
4952
|
+
JOIN pg_namespace n ON n.oid = p.pronamespace
|
|
4953
|
+
JOIN LATERAL aclexplode(coalesce(p.proacl, acldefault('f', p.proowner))) ae ON true
|
|
4954
|
+
WHERE ${filterSchema("n.nspname")}
|
|
4955
|
+
AND ae.grantee <> p.proowner
|
|
4956
|
+
GROUP BY "grantor", "grantee", "schema", "name", "target"
|
|
4957
|
+
|
|
4958
|
+
UNION ALL
|
|
4959
|
+
|
|
4960
|
+
SELECT
|
|
4961
|
+
(ae.grantor)::regrole "grantor",
|
|
4962
|
+
CASE
|
|
4963
|
+
WHEN ae.grantee = 0 THEN 'PUBLIC'
|
|
4964
|
+
ELSE (ae.grantee)::regrole::text
|
|
4965
|
+
END "grantee",
|
|
4966
|
+
n.nspname "schema",
|
|
4967
|
+
t.typname "name",
|
|
4968
|
+
CASE WHEN t.typtype = 'd' THEN 'domains' ELSE 'types' END "target",
|
|
4969
|
+
array_agg(ae.privilege_type ORDER BY ae.privilege_type) "privileges",
|
|
4970
|
+
array_agg(ae.is_grantable ORDER BY ae.privilege_type) "isGrantables"
|
|
4971
|
+
FROM pg_type t
|
|
4972
|
+
JOIN pg_namespace n ON n.oid = t.typnamespace
|
|
4973
|
+
JOIN LATERAL aclexplode(coalesce(t.typacl, acldefault('T', t.typowner))) ae ON true
|
|
4974
|
+
WHERE t.typtype IN ('c', 'd', 'e', 'm', 'r')
|
|
4975
|
+
AND ${filterSchema("n.nspname")}
|
|
4976
|
+
AND ae.grantee <> t.typowner
|
|
4977
|
+
GROUP BY "grantor", "grantee", "schema", "name", "target"
|
|
4978
|
+
|
|
4979
|
+
UNION ALL
|
|
4980
|
+
|
|
4981
|
+
SELECT
|
|
4982
|
+
(ae.grantor)::regrole "grantor",
|
|
4983
|
+
CASE
|
|
4984
|
+
WHEN ae.grantee = 0 THEN 'PUBLIC'
|
|
4985
|
+
ELSE (ae.grantee)::regrole::text
|
|
4986
|
+
END "grantee",
|
|
4987
|
+
NULL "schema",
|
|
4988
|
+
n.nspname "name",
|
|
4989
|
+
'schemas' "target",
|
|
4990
|
+
array_agg(ae.privilege_type ORDER BY ae.privilege_type) "privileges",
|
|
4991
|
+
array_agg(ae.is_grantable ORDER BY ae.privilege_type) "isGrantables"
|
|
4992
|
+
FROM pg_namespace n
|
|
4993
|
+
JOIN LATERAL aclexplode(coalesce(n.nspacl, acldefault('n', n.nspowner))) ae ON true
|
|
4994
|
+
WHERE ${filterSchema("n.nspname")}
|
|
4995
|
+
AND ae.grantee <> n.nspowner
|
|
4996
|
+
GROUP BY "grantor", "grantee", "schema", "name", "target"
|
|
4997
|
+
|
|
4998
|
+
UNION ALL
|
|
4999
|
+
|
|
5000
|
+
SELECT
|
|
5001
|
+
(ae.grantor)::regrole "grantor",
|
|
5002
|
+
CASE
|
|
5003
|
+
WHEN ae.grantee = 0 THEN 'PUBLIC'
|
|
5004
|
+
ELSE (ae.grantee)::regrole::text
|
|
5005
|
+
END "grantee",
|
|
5006
|
+
NULL "schema",
|
|
5007
|
+
d.datname "name",
|
|
5008
|
+
'databases' "target",
|
|
5009
|
+
array_agg(ae.privilege_type ORDER BY ae.privilege_type) "privileges",
|
|
5010
|
+
array_agg(ae.is_grantable ORDER BY ae.privilege_type) "isGrantables"
|
|
5011
|
+
FROM pg_database d
|
|
5012
|
+
JOIN LATERAL aclexplode(coalesce(d.datacl, acldefault('d', d.datdba))) ae ON true
|
|
5013
|
+
WHERE NOT d.datistemplate
|
|
5014
|
+
AND ae.grantee <> d.datdba
|
|
5015
|
+
GROUP BY "grantor", "grantee", "schema", "name", "target"
|
|
5016
|
+
) t`;
|
|
5017
|
+
const policiesSql = `SELECT
|
|
5018
|
+
n.nspname AS "schemaName",
|
|
5019
|
+
c.relname AS "tableName",
|
|
5020
|
+
p.polname AS "name",
|
|
5021
|
+
CASE WHEN p.polpermissive
|
|
5022
|
+
THEN 'PERMISSIVE'
|
|
5023
|
+
ELSE 'RESTRICTIVE'
|
|
5024
|
+
END AS "mode",
|
|
5025
|
+
CASE p.polcmd
|
|
5026
|
+
WHEN '*' THEN 'ALL'
|
|
5027
|
+
WHEN 'r' THEN 'SELECT'
|
|
5028
|
+
WHEN 'a' THEN 'INSERT'
|
|
5029
|
+
WHEN 'w' THEN 'UPDATE'
|
|
5030
|
+
WHEN 'd' THEN 'DELETE'
|
|
5031
|
+
END AS "command",
|
|
5032
|
+
ARRAY(
|
|
5033
|
+
SELECT COALESCE(r.rolname, 'public')
|
|
5034
|
+
FROM unnest(p.polroles) roleOid
|
|
5035
|
+
LEFT JOIN pg_roles r ON r.oid = roleOid
|
|
5036
|
+
ORDER BY roleOid = 0 DESC, COALESCE(r.rolname, 'public')
|
|
5037
|
+
) AS "roles",
|
|
5038
|
+
pg_get_expr(p.polqual, p.polrelid) AS "using",
|
|
5039
|
+
pg_get_expr(p.polwithcheck, p.polrelid) AS "withCheck"
|
|
5040
|
+
FROM pg_policy p
|
|
5041
|
+
JOIN pg_class c ON c.oid = p.polrelid
|
|
5042
|
+
JOIN pg_namespace n ON n.oid = c.relnamespace
|
|
5043
|
+
ORDER BY n.nspname, c.relname, p.polname`;
|
|
5044
|
+
const sql = (version, params) => `SELECT (${schemasSql}) AS "schemas", ${jsonAgg(tablesSql(params?.rls), "tables")}, ${jsonAgg(viewsSql, "views")}, ${jsonAgg(indexesSql, "indexes")}, ${jsonAgg(constraintsSql, "constraints")}, ${jsonAgg(triggersSql, "triggers")}, ${jsonAgg(extensionsSql, "extensions")}, ${jsonAgg(enumsSql, "enums")}, ${jsonAgg(domainsSql, "domains")}, ${jsonAgg(collationsSql(version), "collations")}${params?.roles ? `, (${roleSql(params.roles)}) AS "roles"` : ""}${params?.loadDefaultPrivileges ? `, (${defaultPrivilegesSql}) AS "defaultPrivileges"` : ""}${params?.loadGrants ? `, (${grantsSql}) AS "grants"` : ""}${params?.rls ? `, ${jsonAgg(policiesSql, "policies")}` : ""}`;
|
|
4538
5045
|
async function getDbVersion(db) {
|
|
4539
5046
|
const { rows: [{ version: versionString }] } = await db.query("SELECT version()");
|
|
4540
5047
|
return +versionString.match(/\d+/)[0];
|
|
@@ -4610,9 +5117,36 @@ async function introspectDbSchema(db, params) {
|
|
|
4610
5117
|
}));
|
|
4611
5118
|
}
|
|
4612
5119
|
}
|
|
5120
|
+
if (raw.policies) {
|
|
5121
|
+
const policiesByTable = raw.policies.reduce((acc, policy) => {
|
|
5122
|
+
nullsToUndefined(policy);
|
|
5123
|
+
const key = `${policy.schemaName}.${policy.tableName}`;
|
|
5124
|
+
const existing = acc.get(key);
|
|
5125
|
+
const mappedPolicy = {
|
|
5126
|
+
schemaName: policy.schemaName,
|
|
5127
|
+
tableName: policy.tableName,
|
|
5128
|
+
name: policy.name,
|
|
5129
|
+
mode: policy.mode,
|
|
5130
|
+
command: policy.command,
|
|
5131
|
+
roles: policy.roles,
|
|
5132
|
+
using: policy.using,
|
|
5133
|
+
withCheck: policy.withCheck
|
|
5134
|
+
};
|
|
5135
|
+
if (existing) existing.push(mappedPolicy);
|
|
5136
|
+
else acc.set(key, [mappedPolicy]);
|
|
5137
|
+
return acc;
|
|
5138
|
+
}, /* @__PURE__ */ new Map());
|
|
5139
|
+
for (const table of raw.tables) {
|
|
5140
|
+
if (!table.rls) continue;
|
|
5141
|
+
table.rls.policies = policiesByTable.get(`${table.schemaName}.${table.name}`) ?? [];
|
|
5142
|
+
}
|
|
5143
|
+
}
|
|
5144
|
+
const grants = raw.grants?.map(mapRawGrant);
|
|
5145
|
+
const { policies: _policies, grants: _grants, ...structureWithoutPolicies } = raw;
|
|
4613
5146
|
return {
|
|
4614
5147
|
version,
|
|
4615
|
-
...
|
|
5148
|
+
...structureWithoutPolicies,
|
|
5149
|
+
grants,
|
|
4616
5150
|
defaultPrivileges: raw.defaultPrivileges && [...raw.defaultPrivileges.reduce((acc, privilege) => {
|
|
4617
5151
|
nullsToUndefined(privilege);
|
|
4618
5152
|
const key = `${privilege.grantor}.${privilege.grantee}.${privilege.schema}`;
|
|
@@ -4637,6 +5171,20 @@ async function introspectDbSchema(db, params) {
|
|
|
4637
5171
|
}, /* @__PURE__ */ new Map()).values()]
|
|
4638
5172
|
};
|
|
4639
5173
|
}
|
|
5174
|
+
const mapRawGrant = (raw) => {
|
|
5175
|
+
nullsToUndefined(raw);
|
|
5176
|
+
const privileges = [];
|
|
5177
|
+
const grantablePrivileges = [];
|
|
5178
|
+
for (let i = 0; i < raw.privileges.length; i++) (raw.isGrantables[i] ? grantablePrivileges : privileges).push(raw.privileges[i]);
|
|
5179
|
+
const grant = {
|
|
5180
|
+
to: [raw.grantee],
|
|
5181
|
+
grantedBy: raw.grantor,
|
|
5182
|
+
[raw.target]: raw.schema ? [`${raw.schema}.${raw.name}`] : [raw.name]
|
|
5183
|
+
};
|
|
5184
|
+
if (privileges.length) grant.privileges = privileges;
|
|
5185
|
+
if (grantablePrivileges.length) grant.grantablePrivileges = grantablePrivileges;
|
|
5186
|
+
return grant;
|
|
5187
|
+
};
|
|
4640
5188
|
const nullsToUndefined = (obj) => {
|
|
4641
5189
|
for (const key in obj) if (obj[key] === null) obj[key] = void 0;
|
|
4642
5190
|
};
|
|
@@ -4661,7 +5209,7 @@ const makeStructureToAstCtx = (config, currentSchema) => ({
|
|
|
4661
5209
|
});
|
|
4662
5210
|
const structureToAst = async (ctx, adapter, config) => {
|
|
4663
5211
|
const ast = [];
|
|
4664
|
-
const data = await introspectDbSchema(adapter);
|
|
5212
|
+
const data = await introspectDbSchema(adapter, { rls: true });
|
|
4665
5213
|
for (const name of data.schemas) {
|
|
4666
5214
|
if (name === "public") continue;
|
|
4667
5215
|
ast.push({
|
|
@@ -4681,6 +5229,18 @@ const structureToAst = async (ctx, adapter, config) => {
|
|
|
4681
5229
|
for (const table of data.tables) {
|
|
4682
5230
|
if (table.name === migrationsTable && table.schemaName === migrationsSchema) continue;
|
|
4683
5231
|
ast.push(tableToAst(ctx, data, table, "create", domains));
|
|
5232
|
+
for (const policy of table.rls?.policies || []) ast.push({
|
|
5233
|
+
type: "policy",
|
|
5234
|
+
action: "create",
|
|
5235
|
+
schema: table.schemaName === ctx.currentSchema ? void 0 : table.schemaName,
|
|
5236
|
+
table: table.name,
|
|
5237
|
+
name: policy.name,
|
|
5238
|
+
as: policy.mode,
|
|
5239
|
+
for: policy.command,
|
|
5240
|
+
to: policy.roles,
|
|
5241
|
+
using: policy.using,
|
|
5242
|
+
withCheck: policy.withCheck
|
|
5243
|
+
});
|
|
4684
5244
|
if (table.rls?.enable) ast.push({
|
|
4685
5245
|
type: "tableRls",
|
|
4686
5246
|
action: "enable",
|