@nest-boot/row-level-security 7.2.3 → 7.2.5

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.
Files changed (38) hide show
  1. package/dist/decorators/policy.decorator.d.ts +1 -1
  2. package/dist/decorators/policy.decorator.js +4 -4
  3. package/dist/decorators/policy.decorator.js.map +1 -1
  4. package/dist/decorators/policy.decorator.spec.js +18 -18
  5. package/dist/decorators/policy.decorator.spec.js.map +1 -1
  6. package/dist/index.spec.js +2 -2
  7. package/dist/index.spec.js.map +1 -1
  8. package/dist/interfaces/policy-options.interface.d.ts +1 -1
  9. package/dist/row-level-security-driver.js +1 -1
  10. package/dist/row-level-security-driver.js.map +1 -1
  11. package/dist/row-level-security-driver.spec.js +4 -4
  12. package/dist/row-level-security-driver.spec.js.map +1 -1
  13. package/dist/row-level-security-migration-generator.js +204 -268
  14. package/dist/row-level-security-migration-generator.js.map +1 -1
  15. package/dist/row-level-security-migration-generator.spec.js +108 -38
  16. package/dist/row-level-security-migration-generator.spec.js.map +1 -1
  17. package/dist/row-level-security-migration.d.ts +1 -1
  18. package/dist/row-level-security-migration.js +1 -1
  19. package/dist/row-level-security-migration.js.map +1 -1
  20. package/dist/row-level-security-migration.spec.js +6 -5
  21. package/dist/row-level-security-migration.spec.js.map +1 -1
  22. package/dist/tsconfig.build.tsbuildinfo +1 -1
  23. package/dist/utils/create-policy-bootstrap-sql-statements.d.ts +2 -2
  24. package/dist/utils/create-policy-bootstrap-sql-statements.js +2 -5
  25. package/dist/utils/create-policy-bootstrap-sql-statements.js.map +1 -1
  26. package/dist/utils/create-policy-role-sql-statements.d.ts +4 -4
  27. package/dist/utils/create-policy-role-sql-statements.js +6 -19
  28. package/dist/utils/create-policy-role-sql-statements.js.map +1 -1
  29. package/dist/utils/normalize-postgres-type-alias.spec.d.ts +1 -0
  30. package/dist/utils/normalize-postgres-type-alias.spec.js +28 -0
  31. package/dist/utils/normalize-postgres-type-alias.spec.js.map +1 -0
  32. package/dist/utils/{normalize-postgres-type-alias.js → normalize-postgres-type-alias.util.js} +1 -1
  33. package/dist/utils/normalize-postgres-type-alias.util.js.map +1 -0
  34. package/dist/utils/policy-migration-sql.spec.js +19 -33
  35. package/dist/utils/policy-migration-sql.spec.js.map +1 -1
  36. package/package.json +6 -3
  37. package/dist/utils/normalize-postgres-type-alias.js.map +0 -1
  38. /package/dist/utils/{normalize-postgres-type-alias.d.ts → normalize-postgres-type-alias.util.d.ts} +0 -0
@@ -3,6 +3,7 @@ Object.defineProperty(exports, "__esModule", { value: true });
3
3
  exports.RowLevelSecurityMigrationGenerator = void 0;
4
4
  const node_crypto_1 = require("node:crypto");
5
5
  const migrations_1 = require("@mikro-orm/migrations");
6
+ const pgsql_ast_parser_1 = require("pgsql-ast-parser");
6
7
  const policy_decorator_1 = require("./decorators/policy.decorator");
7
8
  const policy_command_enum_1 = require("./enums/policy-command.enum");
8
9
  const policy_mode_enum_1 = require("./enums/policy-mode.enum");
@@ -11,8 +12,7 @@ const create_policy_down_sql_1 = require("./utils/create-policy-down-sql");
11
12
  const create_policy_privilege_down_sql_statements_1 = require("./utils/create-policy-privilege-down-sql-statements");
12
13
  const create_policy_role_sql_statements_1 = require("./utils/create-policy-role-sql-statements");
13
14
  const create_policy_up_sql_statements_1 = require("./utils/create-policy-up-sql-statements");
14
- const is_postgres_keyword_requiring_quote_1 = require("./utils/is-postgres-keyword-requiring-quote");
15
- const normalize_postgres_type_alias_1 = require("./utils/normalize-postgres-type-alias");
15
+ const normalize_postgres_type_alias_util_1 = require("./utils/normalize-postgres-type-alias.util");
16
16
  const POSTGRES_IDENTIFIER_MAX_LENGTH = 63;
17
17
  const POLICY_IDENTIFIER_TYPE = "policy";
18
18
  /** MikroORM TypeScript migration generator that injects generated RLS SQL. */
@@ -262,10 +262,8 @@ function hasSamePolicyDefinitionContent(left, right) {
262
262
  return (normalizePolicyMode(left.mode) === normalizePolicyMode(right.mode) &&
263
263
  normalizePolicyCommand(left.command) ===
264
264
  normalizePolicyCommand(right.command) &&
265
- normalizePolicyExpression(left.using) ===
266
- normalizePolicyExpression(right.using) &&
267
- normalizePolicyExpression(left.withCheck) ===
268
- normalizePolicyExpression(right.withCheck) &&
265
+ hasSamePolicyExpression(left.using, right.using) &&
266
+ hasSamePolicyExpression(left.withCheck, right.withCheck) &&
269
267
  normalizePolicyRoles(left.roles).join("\n") ===
270
268
  normalizePolicyRoles(right.roles).join("\n"));
271
269
  }
@@ -275,6 +273,17 @@ function normalizePolicyMode(mode) {
275
273
  function normalizePolicyCommand(command) {
276
274
  return command ?? policy_command_enum_1.PolicyCommand.ALL;
277
275
  }
276
+ function hasSamePolicyExpression(left, right) {
277
+ const normalizedLeft = normalizePolicyExpression(left);
278
+ const normalizedRight = normalizePolicyExpression(right);
279
+ if (normalizedLeft !== undefined && normalizedRight !== undefined) {
280
+ return normalizedLeft === normalizedRight;
281
+ }
282
+ if (normalizedLeft === undefined && normalizedRight === undefined) {
283
+ return left?.trim() === right?.trim();
284
+ }
285
+ return false;
286
+ }
278
287
  function normalizePolicyExpression(expression) {
279
288
  const normalized = expression?.trim();
280
289
  if (!normalized) {
@@ -283,270 +292,96 @@ function normalizePolicyExpression(expression) {
283
292
  return normalizePostgresExpression(normalized);
284
293
  }
285
294
  function normalizePostgresExpression(expression) {
286
- const normalized = stripSelectProjectionAliases(mapSqlOutsideQuotedTokens(expression.replace(/'((?:''|[^'])*)'::text\b/gi, "'$1'"), normalizePostgresExpressionSegment).trim());
287
- return stripRedundantJsonOperandParentheses(stripRedundantOuterParentheses(normalized));
288
- }
289
- function normalizePostgresExpressionSegment(segment) {
290
- return normalizeSqlOperatorSpacing(normalizeNullTypeCasts(segment
291
- .replace(/\s+/g, " ")
292
- .replace(/\b(select|and|or|not|is|true|false|null|as)\b/gi, (match) => match.toLowerCase())
293
- .replace(/\s*,\s*/g, ", ")))
294
- .replace(/\(\s+/g, "(")
295
- .replace(/\s+\)/g, ")");
296
- }
297
- function normalizeNullTypeCasts(segment) {
298
- return segment
299
- .replace(/\bnull::(?:character\s+varying|varchar)(?:\s*\([^)]*\))?/gi, "null::character varying")
300
- .replace(/\bnull::(?:timestamp\s+with\s+time\s+zone|timestamptz)\b/gi, "null::timestamp with time zone")
301
- .replace(/\bnull::([a-z_][a-z0-9_.]*)/gi, (_match, type) => `null::${(0, normalize_postgres_type_alias_1.normalizePostgresTypeAlias)(type.toLowerCase())}`);
302
- }
303
- function normalizeSqlOperatorSpacing(segment) {
304
- const jsonOperators = [];
305
- const protectedSegment = segment.replace(/\s*(#>>|#>|->>|->|@>|<@)\s*/g, (_match, operator) => {
306
- const placeholder = `__rls_json_operator_${String(jsonOperators.length)}__`;
307
- jsonOperators.push(` ${operator} `);
308
- return placeholder;
309
- });
310
- return protectedSegment
311
- .replace(/\s*(<>|!=|<=|>=|=|<|>)\s*/g, (_match, operator) => ` ${operator === "!=" ? "<>" : operator} `)
312
- .replace(/__rls_json_operator_(\d+)__/g, (_match, index) => jsonOperators[Number(index)]);
313
- }
314
- function stripSelectProjectionAliases(expression) {
315
- let normalized = "";
316
- const selectDepths = new Set();
317
- let depth = 0;
318
- let index = 0;
319
- while (index < expression.length) {
320
- const character = expression[index];
321
- if (character === "'") {
322
- const literalEnd = readSqlStringLiteralEnd(expression, index);
323
- normalized += expression.slice(index, literalEnd);
324
- index = literalEnd;
325
- continue;
326
- }
327
- if (character === '"') {
328
- const identifierEnd = readQuotedIdentifierEnd(expression, index);
329
- normalized += expression.slice(index, identifierEnd);
330
- index = identifierEnd;
331
- continue;
332
- }
333
- if (character === "(") {
334
- depth += 1;
335
- normalized += character;
336
- index += 1;
337
- continue;
338
- }
339
- if (character === ")") {
340
- selectDepths.delete(depth);
341
- depth -= 1;
342
- normalized += character;
343
- index += 1;
344
- continue;
345
- }
346
- const wordEnd = readSqlWordEnd(expression, index);
347
- if (wordEnd > index) {
348
- const word = expression.slice(index, wordEnd);
349
- const lowerWord = word.toLowerCase();
350
- if (lowerWord === "select") {
351
- selectDepths.add(depth);
352
- }
353
- if (lowerWord === "as" && selectDepths.has(depth)) {
354
- const aliasEnd = readSelectProjectionAliasEnd(expression, wordEnd);
355
- if (aliasEnd !== undefined) {
356
- normalized = normalized.replace(/\s+$/, "");
357
- index = aliasEnd;
358
- continue;
359
- }
360
- }
361
- normalized += word;
362
- index = wordEnd;
363
- continue;
364
- }
365
- normalized += character;
366
- index += 1;
295
+ const parsed = parsePolicyExpressionAst(expression);
296
+ if (!parsed) {
297
+ return undefined;
367
298
  }
368
- return normalized;
299
+ return JSON.stringify(canonicalizePolicyExpressionAst(parsed.ast, parsed.sql));
369
300
  }
370
- function readSelectProjectionAliasEnd(expression, startIndex) {
371
- const aliasStart = skipSqlWhitespace(expression, startIndex);
372
- const aliasEnd = readSqlIdentifierEnd(expression, aliasStart);
373
- if (aliasEnd === aliasStart) {
301
+ function parsePolicyExpressionAst(expression) {
302
+ const parsed = parsePolicyExpressionStatement(expression);
303
+ if (parsed?.statement.type !== "select" || !parsed.statement.where) {
374
304
  return undefined;
375
305
  }
376
- const nextIndex = skipSqlWhitespace(expression, aliasEnd);
377
- return expression[nextIndex] === ")" ? nextIndex : undefined;
378
- }
379
- function stripRedundantOuterParentheses(expression) {
380
- let normalized = expression;
381
- while (isWrappedInParentheses(normalized)) {
382
- normalized = normalized.slice(1, -1).trim();
383
- }
384
- return normalized;
385
- }
386
- function stripRedundantJsonOperandParentheses(expression) {
387
- let normalized = expression;
388
- let previous;
389
- do {
390
- previous = normalized;
391
- normalized = stripRedundantJsonOperandParenthesesOnce(normalized);
392
- } while (normalized !== previous);
393
- return normalized;
394
- }
395
- function stripRedundantJsonOperandParenthesesOnce(expression) {
396
- const removals = new Set();
397
- const parenthesisPairs = findParenthesisPairs(expression);
398
- for (const [start, end] of parenthesisPairs) {
399
- const innerExpression = expression.slice(start + 1, end);
400
- if (hasTopLevelJsonOperator(innerExpression) &&
401
- (hasComparisonOperatorBefore(expression, start) ||
402
- hasComparisonOperatorAfter(expression, end))) {
403
- removals.add(start);
404
- removals.add(end);
405
- }
406
- }
407
- if (removals.size === 0) {
408
- return expression;
409
- }
410
- return [...expression]
411
- .filter((_character, index) => !removals.has(index))
412
- .join("");
306
+ return {
307
+ ast: parsed.statement.where,
308
+ sql: parsed.sql,
309
+ };
413
310
  }
414
- function isWrappedInParentheses(expression) {
415
- if (!expression.startsWith("(") || !expression.endsWith(")")) {
416
- return false;
417
- }
418
- let depth = 0;
419
- let index = 0;
420
- while (index < expression.length) {
421
- const character = expression[index];
422
- if (character === "'") {
423
- index = readSqlStringLiteralEnd(expression, index);
424
- continue;
425
- }
426
- if (character === '"') {
427
- index = readQuotedIdentifierEnd(expression, index);
428
- continue;
429
- }
430
- if (character === "(") {
431
- depth += 1;
432
- }
433
- else if (character === ")") {
434
- depth -= 1;
435
- if (depth === 0 && index < expression.length - 1) {
436
- return false;
437
- }
438
- }
439
- if (depth < 0) {
440
- return false;
441
- }
442
- index += 1;
311
+ function parsePolicyExpressionStatement(expression) {
312
+ const sql = `select 1 where ${expression}`;
313
+ try {
314
+ return {
315
+ sql,
316
+ statement: (0, pgsql_ast_parser_1.parse)(sql, { locationTracking: true })[0],
317
+ };
443
318
  }
444
- return depth === 0;
445
- }
446
- function findParenthesisPairs(expression) {
447
- const pairs = [];
448
- const stack = [];
449
- let index = 0;
450
- while (index < expression.length) {
451
- const character = expression[index];
452
- if (character === "'") {
453
- index = readSqlStringLiteralEnd(expression, index);
454
- continue;
455
- }
456
- if (character === '"') {
457
- index = readQuotedIdentifierEnd(expression, index);
458
- continue;
459
- }
460
- if (character === "(") {
461
- stack.push(index);
462
- }
463
- else if (character === ")") {
464
- const start = stack.pop();
465
- if (start !== undefined) {
466
- pairs.push([start, index]);
319
+ catch {
320
+ const parserCompatibleExpression = getPolicyExpressionForAstParser(expression);
321
+ if (parserCompatibleExpression !== expression) {
322
+ try {
323
+ const parserCompatibleSql = `select 1 where ${parserCompatibleExpression}`;
324
+ return {
325
+ sql: parserCompatibleSql,
326
+ statement: (0, pgsql_ast_parser_1.parse)(parserCompatibleSql, {
327
+ locationTracking: true,
328
+ })[0],
329
+ };
467
330
  }
468
- }
469
- index += 1;
470
- }
471
- return pairs;
472
- }
473
- function hasTopLevelJsonOperator(expression) {
474
- let depth = 0;
475
- let index = 0;
476
- while (index < expression.length) {
477
- const character = expression[index];
478
- if (character === "'") {
479
- index = readSqlStringLiteralEnd(expression, index);
480
- continue;
481
- }
482
- if (character === '"') {
483
- index = readQuotedIdentifierEnd(expression, index);
484
- continue;
485
- }
486
- if (character === "(") {
487
- depth += 1;
488
- index += 1;
489
- continue;
490
- }
491
- if (character === ")") {
492
- depth -= 1;
493
- index += 1;
494
- continue;
495
- }
496
- if (depth === 0) {
497
- const jsonOperator = readJsonOperatorAt(expression, index);
498
- if (jsonOperator) {
499
- return true;
331
+ catch {
332
+ return undefined;
500
333
  }
501
334
  }
502
- index += 1;
335
+ return undefined;
503
336
  }
504
- return false;
505
337
  }
506
- function hasComparisonOperatorBefore(expression, startIndex) {
507
- const operatorEnd = skipSqlWhitespaceBackwards(expression, startIndex - 1) + 1;
508
- return ["<>", "!=", "<=", ">=", "=", "<", ">"].some((operator) => expression.slice(operatorEnd - operator.length, operatorEnd) === operator);
338
+ function getPolicyExpressionForAstParser(expression) {
339
+ // pgsql-ast-parser treats these multi-word casts in select projections as ambiguous.
340
+ return replaceSqlOutsideQuotedTokens(expression, (segment) => segment
341
+ .replace(/::\s*character\s+varying\b/gi, "::varchar")
342
+ .replace(/::\s*bit\s+varying\b/gi, "::varbit"));
509
343
  }
510
- function hasComparisonOperatorAfter(expression, endIndex) {
511
- const operatorStart = skipSqlWhitespace(expression, endIndex + 1);
512
- return Boolean(readComparisonOperatorAt(expression, operatorStart));
513
- }
514
- function mapSqlOutsideQuotedTokens(expression, transform) {
344
+ function replaceSqlOutsideQuotedTokens(expression, transform) {
515
345
  const parts = [];
516
346
  let segmentStart = 0;
517
347
  let index = 0;
518
348
  while (index < expression.length) {
519
- if (expression[index] !== "'" && expression[index] !== '"') {
349
+ const tokenEnd = readSqlQuotedTokenEnd(expression, index);
350
+ if (tokenEnd === undefined) {
520
351
  index += 1;
521
352
  continue;
522
353
  }
523
354
  parts.push(transform(expression.slice(segmentStart, index)));
524
- if (expression[index] === "'") {
525
- const literalStart = index;
526
- index = readSqlStringLiteralEnd(expression, index);
527
- parts.push(expression.slice(literalStart, index));
528
- }
529
- else {
530
- const identifierStart = index;
531
- index = readQuotedIdentifierEnd(expression, index);
532
- parts.push(normalizeQuotedIdentifier(expression.slice(identifierStart, index)));
533
- }
534
- segmentStart = index;
355
+ parts.push(expression.slice(index, tokenEnd));
356
+ segmentStart = tokenEnd;
357
+ index = tokenEnd;
535
358
  }
536
359
  parts.push(transform(expression.slice(segmentStart)));
537
360
  return parts.join("");
538
361
  }
539
- function normalizeQuotedIdentifier(identifierToken) {
540
- const identifier = identifierToken.slice(1, -1).replace(/""/g, '"');
541
- if (/^[a-z_][a-z0-9_]*$/.test(identifier) &&
542
- !(0, is_postgres_keyword_requiring_quote_1.isPostgresKeywordRequiringQuote)(identifier)) {
543
- return identifier;
362
+ function readSqlQuotedTokenEnd(expression, startIndex) {
363
+ if (expression[startIndex] === "'") {
364
+ return readSqlStringLiteralEnd(expression, startIndex, hasPostgresEscapeStringPrefix(expression, startIndex));
365
+ }
366
+ if (expression[startIndex] === '"') {
367
+ return readQuotedIdentifierEnd(expression, startIndex);
368
+ }
369
+ return readDollarQuotedStringEnd(expression, startIndex);
370
+ }
371
+ function hasPostgresEscapeStringPrefix(expression, quoteIndex) {
372
+ const prefixIndex = quoteIndex - 1;
373
+ if (!/[eE]/.test(expression[prefixIndex] ?? "")) {
374
+ return false;
544
375
  }
545
- return identifierToken;
376
+ return prefixIndex === 0 || !isSqlIdentifierPart(expression[prefixIndex - 1]);
546
377
  }
547
- function readSqlStringLiteralEnd(expression, startIndex) {
378
+ function readSqlStringLiteralEnd(expression, startIndex, allowBackslashEscapes = false) {
548
379
  let index = startIndex + 1;
549
380
  while (index < expression.length) {
381
+ if (allowBackslashEscapes && expression[index] === "\\") {
382
+ index += 2;
383
+ continue;
384
+ }
550
385
  if (expression[index] !== "'") {
551
386
  index += 1;
552
387
  continue;
@@ -560,6 +395,9 @@ function readSqlStringLiteralEnd(expression, startIndex) {
560
395
  }
561
396
  return index;
562
397
  }
398
+ function isSqlIdentifierPart(character) {
399
+ return character !== undefined && /[A-Za-z0-9_$]/.test(character);
400
+ }
563
401
  function readQuotedIdentifierEnd(expression, startIndex) {
564
402
  let index = startIndex + 1;
565
403
  while (index < expression.length) {
@@ -576,41 +414,139 @@ function readQuotedIdentifierEnd(expression, startIndex) {
576
414
  }
577
415
  return index;
578
416
  }
579
- function readSqlIdentifierEnd(expression, startIndex) {
580
- if (expression[startIndex] === '"') {
581
- return readQuotedIdentifierEnd(expression, startIndex);
417
+ function readDollarQuotedStringEnd(expression, startIndex) {
418
+ const delimiter = /^\$[a-z_][a-z0-9_]*\$|^\$\$/i.exec(expression.slice(startIndex))?.[0];
419
+ if (!delimiter) {
420
+ return undefined;
582
421
  }
583
- return readSqlWordEnd(expression, startIndex);
422
+ const endIndex = expression.indexOf(delimiter, startIndex + delimiter.length);
423
+ return endIndex === -1 ? expression.length : endIndex + delimiter.length;
584
424
  }
585
- function readJsonOperatorAt(expression, startIndex) {
586
- return ["#>>", "->>", "#>", "->", "@>", "<@"].find((operator) => expression.startsWith(operator, startIndex));
587
- }
588
- function readComparisonOperatorAt(expression, startIndex) {
589
- return ["<>", "!=", "<=", ">=", "=", "<", ">"].find((operator) => expression.startsWith(operator, startIndex));
425
+ function canonicalizePolicyExpressionAst(value, sourceSql) {
426
+ if (value === null || typeof value !== "object") {
427
+ return value === undefined
428
+ ? null
429
+ : value;
430
+ }
431
+ if (Array.isArray(value)) {
432
+ return value.map((item) => canonicalizePolicyExpressionAst(item, sourceSql));
433
+ }
434
+ if (isRedundantTextCast(value)) {
435
+ return canonicalizePolicyExpressionAst(value.operand, sourceSql);
436
+ }
437
+ const node = value;
438
+ const entries = [];
439
+ for (const key of Object.keys(node).sort()) {
440
+ const property = node[key];
441
+ if (property === undefined || key === "_location") {
442
+ continue;
443
+ }
444
+ if (key === "alias" && "expr" in node) {
445
+ continue;
446
+ }
447
+ if (key === "value" && isNumericPolicyExpressionAstNode(node)) {
448
+ entries.push([
449
+ key,
450
+ getPolicyExpressionAstNodeSource(node, sourceSql) ??
451
+ canonicalizePolicyExpressionAst(property, sourceSql),
452
+ ]);
453
+ continue;
454
+ }
455
+ if (key === "op" && typeof property === "string") {
456
+ entries.push([key, normalizePolicyExpressionOperator(property)]);
457
+ continue;
458
+ }
459
+ if (key === "to" && node.type === "cast") {
460
+ entries.push([
461
+ key,
462
+ canonicalizePolicyExpressionDataType(property, sourceSql),
463
+ ]);
464
+ continue;
465
+ }
466
+ entries.push([key, canonicalizePolicyExpressionAst(property, sourceSql)]);
467
+ }
468
+ return Object.fromEntries(entries);
590
469
  }
591
- function readSqlWordEnd(expression, startIndex) {
592
- if (!/[a-z_]/i.test(expression[startIndex] ?? "")) {
593
- return startIndex;
470
+ function canonicalizePolicyExpressionDataType(value, sourceSql) {
471
+ if (value === null || typeof value !== "object") {
472
+ return value === undefined
473
+ ? null
474
+ : value;
594
475
  }
595
- let index = startIndex + 1;
596
- while (/[a-z0-9_$]/i.test(expression[index] ?? "")) {
597
- index += 1;
476
+ if (Array.isArray(value)) {
477
+ return value.map((item) => canonicalizePolicyExpressionDataType(item, sourceSql));
598
478
  }
599
- return index;
479
+ const node = value;
480
+ const entries = [];
481
+ const isDoubleQuoted = node.doubleQuoted === true;
482
+ for (const key of Object.keys(node).sort()) {
483
+ const property = node[key];
484
+ if (property === undefined || key === "_location") {
485
+ continue;
486
+ }
487
+ if (key === "name" && typeof property === "string" && !isDoubleQuoted) {
488
+ entries.push([key, normalizePolicyExpressionDataTypeName(property)]);
489
+ continue;
490
+ }
491
+ if (key === "arrayOf") {
492
+ entries.push([
493
+ key,
494
+ canonicalizePolicyExpressionDataType(property, sourceSql),
495
+ ]);
496
+ continue;
497
+ }
498
+ entries.push([key, canonicalizePolicyExpressionAst(property, sourceSql)]);
499
+ }
500
+ return Object.fromEntries(entries);
600
501
  }
601
- function skipSqlWhitespace(expression, startIndex) {
602
- let index = startIndex;
603
- while (/\s/.test(expression[index] ?? "")) {
604
- index += 1;
502
+ function isRedundantTextCast(value) {
503
+ if (!isPolicyExpressionAstNode(value, "cast") || !isTextType(value.to)) {
504
+ return false;
605
505
  }
606
- return index;
506
+ return (isPolicyExpressionAstNode(value.operand, "string") ||
507
+ isTextJsonMember(value.operand));
607
508
  }
608
- function skipSqlWhitespaceBackwards(expression, startIndex) {
609
- let index = startIndex;
610
- while (/\s/.test(expression[index] ?? "")) {
611
- index -= 1;
509
+ function isTextJsonMember(value) {
510
+ return isPolicyExpressionAstNode(value, "member") && value.op === "->>";
511
+ }
512
+ function isTextType(value) {
513
+ if (value === null || typeof value !== "object" || Array.isArray(value)) {
514
+ return false;
612
515
  }
613
- return index;
516
+ const node = value;
517
+ const typeName = node.name;
518
+ return (node.doubleQuoted !== true &&
519
+ typeof typeName === "string" &&
520
+ normalizePolicyExpressionDataTypeName(typeName) === "text");
521
+ }
522
+ function isNumericPolicyExpressionAstNode(value) {
523
+ return value.type === "integer" || value.type === "numeric";
524
+ }
525
+ function getPolicyExpressionAstNodeSource(value, sourceSql) {
526
+ const location = value._location;
527
+ if (location === null ||
528
+ typeof location !== "object" ||
529
+ Array.isArray(location)) {
530
+ return undefined;
531
+ }
532
+ const start = location.start;
533
+ const end = location.end;
534
+ if (typeof start !== "number" || typeof end !== "number") {
535
+ return undefined;
536
+ }
537
+ return sourceSql.slice(start, end);
538
+ }
539
+ function isPolicyExpressionAstNode(value, type) {
540
+ return (value !== null &&
541
+ typeof value === "object" &&
542
+ !Array.isArray(value) &&
543
+ value.type === type);
544
+ }
545
+ function normalizePolicyExpressionDataTypeName(typeName) {
546
+ return (0, normalize_postgres_type_alias_util_1.normalizePostgresTypeAlias)(typeName.toLowerCase().replace(/\s+/g, " "));
547
+ }
548
+ function normalizePolicyExpressionOperator(operator) {
549
+ return operator === "!=" ? "<>" : operator;
614
550
  }
615
551
  function normalizePolicyRoles(roles) {
616
552
  return [