@cerbos/orm-prisma 4.0.0 → 5.0.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/README.md +611 -460
- package/lib/collections.d.ts +30 -0
- package/lib/collections.d.ts.map +1 -0
- package/lib/collections.js +421 -0
- package/lib/collections.js.map +1 -0
- package/lib/comparison.d.ts +24 -0
- package/lib/comparison.d.ts.map +1 -0
- package/lib/comparison.js +731 -0
- package/lib/comparison.js.map +1 -0
- package/lib/errors.d.ts +13 -0
- package/lib/errors.d.ts.map +1 -0
- package/lib/errors.js +17 -0
- package/lib/errors.js.map +1 -0
- package/lib/evaluate.d.ts +5 -0
- package/lib/evaluate.d.ts.map +1 -0
- package/lib/evaluate.js +118 -0
- package/lib/evaluate.js.map +1 -0
- package/lib/fields.d.ts +34 -0
- package/lib/fields.d.ts.map +1 -0
- package/lib/fields.js +253 -0
- package/lib/fields.js.map +1 -0
- package/lib/functions.d.ts +5 -0
- package/lib/functions.d.ts.map +1 -0
- package/lib/functions.js +408 -0
- package/lib/functions.js.map +1 -0
- package/lib/hierarchy.d.ts +6 -0
- package/lib/hierarchy.d.ts.map +1 -0
- package/lib/hierarchy.js +266 -0
- package/lib/hierarchy.js.map +1 -0
- package/lib/index.d.ts +91 -8
- package/lib/index.d.ts.map +1 -1
- package/lib/index.js +34 -2301
- package/lib/index.js.map +1 -1
- package/lib/literals.d.ts +20 -0
- package/lib/literals.d.ts.map +1 -0
- package/lib/literals.js +290 -0
- package/lib/literals.js.map +1 -0
- package/lib/mapping.d.ts +95 -0
- package/lib/mapping.d.ts.map +1 -0
- package/lib/mapping.js +316 -0
- package/lib/mapping.js.map +1 -0
- package/lib/narrowing.d.ts +12 -0
- package/lib/narrowing.d.ts.map +1 -0
- package/lib/narrowing.js +32 -0
- package/lib/narrowing.js.map +1 -0
- package/lib/plan.d.ts +49 -0
- package/lib/plan.d.ts.map +1 -0
- package/lib/plan.js +86 -0
- package/lib/plan.js.map +1 -0
- package/lib/presence.d.ts +25 -0
- package/lib/presence.d.ts.map +1 -0
- package/lib/presence.js +82 -0
- package/lib/presence.js.map +1 -0
- package/lib/regex.d.ts +33 -0
- package/lib/regex.d.ts.map +1 -0
- package/lib/regex.js +480 -0
- package/lib/regex.js.map +1 -0
- package/lib/relations.d.ts +58 -0
- package/lib/relations.d.ts.map +1 -0
- package/lib/relations.js +175 -0
- package/lib/relations.js.map +1 -0
- package/lib/rewrite.d.ts +36 -0
- package/lib/rewrite.d.ts.map +1 -0
- package/lib/rewrite.js +366 -0
- package/lib/rewrite.js.map +1 -0
- package/lib/solve.d.ts +20 -0
- package/lib/solve.d.ts.map +1 -0
- package/lib/solve.js +77 -0
- package/lib/solve.js.map +1 -0
- package/lib/strings.d.ts +12 -0
- package/lib/strings.d.ts.map +1 -0
- package/lib/strings.js +110 -0
- package/lib/strings.js.map +1 -0
- package/lib/ternary.d.ts +12 -0
- package/lib/ternary.d.ts.map +1 -0
- package/lib/ternary.js +192 -0
- package/lib/ternary.js.map +1 -0
- package/lib/timestamp.d.ts +45 -0
- package/lib/timestamp.d.ts.map +1 -0
- package/lib/timestamp.js +225 -0
- package/lib/timestamp.js.map +1 -0
- package/lib/translate.d.ts +20 -0
- package/lib/translate.d.ts.map +1 -0
- package/lib/translate.js +287 -0
- package/lib/translate.js.map +1 -0
- package/lib/types.d.ts +29 -0
- package/lib/types.d.ts.map +1 -0
- package/lib/types.js +980 -0
- package/lib/types.js.map +1 -0
- package/lib/value.d.ts +8 -0
- package/lib/value.d.ts.map +1 -0
- package/lib/value.js +7 -0
- package/lib/value.js.map +1 -0
- package/package.json +31 -18
- package/src/adversarial.test.ts +488 -488
- package/src/collections.ts +738 -0
- package/src/comparison.ts +1157 -0
- package/src/corpus.ts +597 -0
- package/src/errors.ts +12 -0
- package/src/evaluate.ts +170 -0
- package/src/fields.ts +373 -0
- package/src/functions.ts +548 -0
- package/src/generated/prisma-adversarial/browser.ts +20 -0
- package/src/generated/prisma-adversarial/client.ts +20 -0
- package/src/generated/prisma-adversarial/commonInputTypes.ts +116 -127
- package/src/generated/prisma-adversarial/internal/class.ts +44 -4
- package/src/generated/prisma-adversarial/internal/prismaNamespace.ts +350 -3
- package/src/generated/prisma-adversarial/internal/prismaNamespaceBrowser.ts +49 -2
- package/src/generated/prisma-adversarial/models/AdversarialBoolListElement.ts +1272 -0
- package/src/generated/prisma-adversarial/models/AdversarialInner.ts +1369 -0
- package/src/generated/prisma-adversarial/models/AdversarialNumberListElement.ts +1316 -0
- package/src/generated/prisma-adversarial/models/AdversarialParent.ts +1474 -0
- package/src/generated/prisma-adversarial/models/AdversarialResource.ts +547 -75
- package/src/generated/prisma-adversarial/models.ts +4 -0
- package/src/generated/prisma-adversarial-mysql/browser.ts +64 -0
- package/src/generated/{prisma → prisma-adversarial-mysql}/client.ts +23 -18
- package/src/generated/{prisma → prisma-adversarial-mysql}/commonInputTypes.ts +167 -92
- package/src/generated/prisma-adversarial-mysql/internal/class.ts +284 -0
- package/src/generated/prisma-adversarial-mysql/internal/prismaNamespace.ts +1518 -0
- package/src/generated/prisma-adversarial-mysql/internal/prismaNamespaceBrowser.ts +274 -0
- package/src/generated/prisma-adversarial-mysql/models/AdversarialBoolListElement.ts +1157 -0
- package/src/generated/prisma-adversarial-mysql/models/AdversarialCategory.ts +1286 -0
- package/src/generated/prisma-adversarial-mysql/models/AdversarialInner.ts +1247 -0
- package/src/generated/prisma-adversarial-mysql/models/AdversarialLabel.ts +1157 -0
- package/src/generated/prisma-adversarial-mysql/models/AdversarialNumberListElement.ts +1201 -0
- package/src/generated/prisma-adversarial-mysql/models/AdversarialParent.ts +1352 -0
- package/src/generated/prisma-adversarial-mysql/models/AdversarialResource.ts +2021 -0
- package/src/generated/prisma-adversarial-mysql/models/AdversarialSubCategory.ts +1286 -0
- package/src/generated/prisma-adversarial-mysql/models/AdversarialTag.ts +1190 -0
- package/src/generated/prisma-adversarial-mysql/models.ts +20 -0
- package/src/generated/prisma-adversarial-mysql-v6/index.d.ts +13822 -0
- package/src/generated/prisma-adversarial-pg/browser.ts +64 -0
- package/src/generated/prisma-adversarial-pg/client.ts +86 -0
- package/src/generated/prisma-adversarial-pg/commonInputTypes.ts +345 -0
- package/src/generated/prisma-adversarial-pg/enums.ts +15 -0
- package/src/generated/prisma-adversarial-pg/internal/class.ts +284 -0
- package/src/generated/prisma-adversarial-pg/internal/prismaNamespace.ts +1542 -0
- package/src/generated/{prisma → prisma-adversarial-pg}/internal/prismaNamespaceBrowser.ts +72 -43
- package/src/generated/prisma-adversarial-pg/models/AdversarialBoolListElement.ts +1275 -0
- package/src/generated/prisma-adversarial-pg/models/AdversarialCategory.ts +1404 -0
- package/src/generated/prisma-adversarial-pg/models/AdversarialInner.ts +1371 -0
- package/src/generated/prisma-adversarial-pg/models/AdversarialLabel.ts +1275 -0
- package/src/generated/prisma-adversarial-pg/models/AdversarialNumberListElement.ts +1319 -0
- package/src/generated/prisma-adversarial-pg/models/AdversarialParent.ts +1476 -0
- package/src/generated/prisma-adversarial-pg/models/AdversarialResource.ts +2139 -0
- package/src/generated/prisma-adversarial-pg/models/AdversarialSubCategory.ts +1404 -0
- package/src/generated/prisma-adversarial-pg/models/AdversarialTag.ts +1310 -0
- package/src/generated/prisma-adversarial-pg/models.ts +20 -0
- package/src/generated/prisma-adversarial-pg-v6/client.d.ts +1 -0
- package/src/generated/prisma-adversarial-pg-v6/default.d.ts +1 -0
- package/src/generated/prisma-adversarial-pg-v6/edge.d.ts +1 -0
- package/src/generated/prisma-adversarial-pg-v6/index.d.ts +14918 -0
- package/src/generated/prisma-adversarial-pg-v6/runtime/index-browser.d.ts +370 -0
- package/src/generated/prisma-adversarial-pg-v6/runtime/library.d.ts +3982 -0
- package/src/generated/prisma-adversarial-pg-v6/wasm.d.ts +1 -0
- package/src/generated/prisma-adversarial-v6/index.d.ts +6515 -304
- package/src/hierarchy.ts +405 -0
- package/src/index.ts +134 -3325
- package/src/literals.ts +363 -0
- package/src/mapping.ts +474 -0
- package/src/narrowing.ts +33 -0
- package/src/plan.ts +125 -0
- package/src/presence.ts +114 -0
- package/src/regex.ts +596 -0
- package/src/relations.ts +226 -0
- package/src/rewrite.ts +524 -0
- package/src/solve.ts +96 -0
- package/src/strings.ts +152 -0
- package/src/ternary.ts +359 -0
- package/src/test-setup.adversarial-url.ts +21 -0
- package/src/test-setup.adversarial.mysql.v6.ts +13 -0
- package/src/test-setup.adversarial.mysql.v7.ts +12 -0
- package/src/test-setup.adversarial.pg.v6.ts +13 -0
- package/src/test-setup.adversarial.pg.v7.ts +9 -0
- package/src/test-setup.adversarial.v7.ts +1 -0
- package/src/timestamp.ts +308 -0
- package/src/translate.ts +419 -0
- package/src/translator.test.ts +766 -0
- package/src/types.ts +1310 -0
- package/src/value.ts +16 -0
- package/src/generated/prisma/browser.ts +0 -59
- package/src/generated/prisma/internal/class.ts +0 -274
- package/src/generated/prisma/internal/prismaNamespace.ts +0 -1403
- package/src/generated/prisma/models/Category.ts +0 -1412
- package/src/generated/prisma/models/Label.ts +0 -1276
- package/src/generated/prisma/models/NestedResource.ts +0 -1562
- package/src/generated/prisma/models/NextLevelNestedResource.ts +0 -1337
- package/src/generated/prisma/models/Resource.ts +0 -2196
- package/src/generated/prisma/models/SubCategory.ts +0 -1412
- package/src/generated/prisma/models/Tag.ts +0 -1276
- package/src/generated/prisma/models/User.ts +0 -1503
- package/src/generated/prisma/models.ts +0 -19
- package/src/generated/prisma-v6/index.d.ts +0 -13726
- package/src/index.test.ts +0 -8885
- package/src/test-setup.v6.ts +0 -5
- package/src/test-setup.v7.ts +0 -7
- /package/src/generated/{prisma → prisma-adversarial-mysql}/enums.ts +0 -0
- /package/src/generated/{prisma-v6 → prisma-adversarial-mysql-v6}/client.d.ts +0 -0
- /package/src/generated/{prisma-v6 → prisma-adversarial-mysql-v6}/default.d.ts +0 -0
- /package/src/generated/{prisma-v6 → prisma-adversarial-mysql-v6}/edge.d.ts +0 -0
- /package/src/generated/{prisma-v6 → prisma-adversarial-mysql-v6}/runtime/index-browser.d.ts +0 -0
- /package/src/generated/{prisma-v6 → prisma-adversarial-mysql-v6}/runtime/library.d.ts +0 -0
- /package/src/generated/{prisma-v6 → prisma-adversarial-mysql-v6}/wasm.d.ts +0 -0
package/src/relations.ts
ADDED
|
@@ -0,0 +1,226 @@
|
|
|
1
|
+
// Nesting a filter through the relations a reference crosses, and the hop-existence guards that
|
|
2
|
+
// keep an absent to-one parent denied under negation.
|
|
3
|
+
|
|
4
|
+
import type { PlanExpressionOperand } from "@cerbos/core";
|
|
5
|
+
|
|
6
|
+
import type { PrismaFilter } from "./index";
|
|
7
|
+
import { resolveFieldReference } from "./mapping";
|
|
8
|
+
import type { RelationConfig, TranslationContext } from "./mapping";
|
|
9
|
+
import { isNamedOperand, isOperatorOperand } from "./plan";
|
|
10
|
+
import { UnsupportedQueryPlanError } from "./errors";
|
|
11
|
+
|
|
12
|
+
/** The Prisma operator that reaches into a relation for an existence test. */
|
|
13
|
+
export function relationOperator(relation: RelationConfig): string {
|
|
14
|
+
return relation.type === "one" ? "is" : "some";
|
|
15
|
+
}
|
|
16
|
+
|
|
17
|
+
/**
|
|
18
|
+
* One relation-scoped filter (`{ tags: { some: … } }`), narrowed by the store-side predicate the
|
|
19
|
+
* caller declared on the mapping.
|
|
20
|
+
*
|
|
21
|
+
* Every nested relation filter the adapter emits goes through here, so a declared
|
|
22
|
+
* `subqueryFilter` reaches the collection macros, the membership shapes, the emptiness checks and
|
|
23
|
+
* the hop-existence guard alike. Adding a new relation shape means routing it through here, or
|
|
24
|
+
* the declaration silently stops applying to it.
|
|
25
|
+
*
|
|
26
|
+
* `every` is the one operator that cannot simply absorb the predicate. `{ every: AND(W, P) }`
|
|
27
|
+
* would REQUIRE every record to satisfy W, which is the opposite of "ignore the records W hides",
|
|
28
|
+
* so it is rewritten to `{ none: AND(W, NOT P) }` — no visible record violates P. Both agree with
|
|
29
|
+
* `every` when nothing is declared, and both stay vacuously true over an empty relation.
|
|
30
|
+
*/
|
|
31
|
+
export function relationFilter(
|
|
32
|
+
relation: RelationConfig,
|
|
33
|
+
operator: string,
|
|
34
|
+
inner: PrismaFilter
|
|
35
|
+
): PrismaFilter {
|
|
36
|
+
const declared = relation.subqueryFilter;
|
|
37
|
+
|
|
38
|
+
if (declared === undefined) {
|
|
39
|
+
return { [relation.name]: { [operator]: inner } };
|
|
40
|
+
}
|
|
41
|
+
|
|
42
|
+
const isEmpty = Object.keys(inner).length === 0;
|
|
43
|
+
|
|
44
|
+
if (operator === "every") {
|
|
45
|
+
return isEmpty
|
|
46
|
+
? { [relation.name]: { every: inner } }
|
|
47
|
+
: { [relation.name]: { none: { AND: [declared, { NOT: inner }] } } };
|
|
48
|
+
}
|
|
49
|
+
|
|
50
|
+
return {
|
|
51
|
+
[relation.name]: {
|
|
52
|
+
[operator]: isEmpty ? declared : { AND: [declared, inner] },
|
|
53
|
+
},
|
|
54
|
+
};
|
|
55
|
+
}
|
|
56
|
+
|
|
57
|
+
/**
|
|
58
|
+
* `filter` nested through every relation in `relations`, outermost first, each reached with its
|
|
59
|
+
* existence operator (`is` for to-one, `some` for to-many). No relations leaves it unchanged.
|
|
60
|
+
*/
|
|
61
|
+
export function wrapInRelations(
|
|
62
|
+
relations: RelationConfig[] | undefined,
|
|
63
|
+
filter: PrismaFilter
|
|
64
|
+
): PrismaFilter {
|
|
65
|
+
let current = filter;
|
|
66
|
+
|
|
67
|
+
for (const relation of [...(relations ?? [])].reverse()) {
|
|
68
|
+
current = relationFilter(relation, relationOperator(relation), current);
|
|
69
|
+
}
|
|
70
|
+
|
|
71
|
+
return current;
|
|
72
|
+
}
|
|
73
|
+
|
|
74
|
+
/**
|
|
75
|
+
* "Every one of these relations exists", as a nested Prisma filter with an empty innermost
|
|
76
|
+
* predicate — `{ parent: { is: { inner: { is: {} } } } }`.
|
|
77
|
+
*/
|
|
78
|
+
function buildHopsExistFilter(
|
|
79
|
+
hops: RelationConfig[]
|
|
80
|
+
): PrismaFilter | undefined {
|
|
81
|
+
return hops.length === 0 ? undefined : wrapInRelations(hops, {});
|
|
82
|
+
}
|
|
83
|
+
|
|
84
|
+
/**
|
|
85
|
+
* "Every intermediate hop of a dotted path exists", as a Prisma filter.
|
|
86
|
+
*
|
|
87
|
+
* CEL cannot dot through a list, so each intermediate segment of `a.b.c` is a to-ONE
|
|
88
|
+
* parent: absent, the caller sends no attribute and CEL raises a missing-path error, which
|
|
89
|
+
* denies. `NOT { categories: { some: { subCategories: { some: P } } } }` is TRUE for a row
|
|
90
|
+
* with no category at all, so the negation returns rows the PDP denies — the parent must be
|
|
91
|
+
* required separately (cerbos/query-plan-adapters#309).
|
|
92
|
+
*
|
|
93
|
+
* Returns undefined when the reference has no intermediate hop, so a direct relation keeps
|
|
94
|
+
* its empty-collection semantics (`!tags.exists(...)` over zero tags is still TRUE).
|
|
95
|
+
*/
|
|
96
|
+
export function buildLeadingHopsExistFilter(
|
|
97
|
+
head: RelationConfig,
|
|
98
|
+
restRelations: RelationConfig[]
|
|
99
|
+
): PrismaFilter | undefined {
|
|
100
|
+
if (restRelations.length === 0) {
|
|
101
|
+
return undefined;
|
|
102
|
+
}
|
|
103
|
+
|
|
104
|
+
// The last entry is the collection being iterated; everything before it is a hop.
|
|
105
|
+
return buildHopsExistFilter([head, ...restRelations.slice(0, -1)]);
|
|
106
|
+
}
|
|
107
|
+
|
|
108
|
+
/**
|
|
109
|
+
* "Every to-one hop this expression dots through exists", as a Prisma filter, or undefined
|
|
110
|
+
* when it dots through none.
|
|
111
|
+
*
|
|
112
|
+
* Prisma has no UNKNOWN: a chained relation filter is exact under the POSITIVE polarity —
|
|
113
|
+
* `categories: { some: { subCategories: { some: P } } }` cannot hold without the category —
|
|
114
|
+
* but `NOT` inverts the hop requirement along with the predicate, so every operator built on
|
|
115
|
+
* a chain readmits the parentless rows once negated. #309 fixed that inside the collection
|
|
116
|
+
* macros, which left the sibling operators reached through the same chain unguarded:
|
|
117
|
+
* membership and `hasIntersection` (cerbos/query-plan-adapters#315), and the negated count
|
|
118
|
+
* spelling `!(size(chain) > 0)` (#316), all of which the macros' guard never saw.
|
|
119
|
+
*
|
|
120
|
+
* Requiring the hops OUTSIDE the negation is what fixes all of them at once, and it is
|
|
121
|
+
* unconditionally faithful to CEL rather than a per-operator patch: a missing hop is a
|
|
122
|
+
* missing-path error, which denies under BOTH polarities, so the requirement never depends
|
|
123
|
+
* on which operator sits above the chain.
|
|
124
|
+
*
|
|
125
|
+
* Lambda bodies are deliberately not walked — their references resolve against a scoped
|
|
126
|
+
* mapper, and a negated macro carries its own guard through the negated collection operators.
|
|
127
|
+
*/
|
|
128
|
+
function buildExpressionHopsExistFilter(
|
|
129
|
+
operand: PlanExpressionOperand,
|
|
130
|
+
context: TranslationContext
|
|
131
|
+
): PrismaFilter | undefined {
|
|
132
|
+
const hops: PrismaFilter[] = [];
|
|
133
|
+
const seen = new Set<string>();
|
|
134
|
+
|
|
135
|
+
const visit = (expr: PlanExpressionOperand): void => {
|
|
136
|
+
if (isNamedOperand(expr)) {
|
|
137
|
+
if (seen.has(expr.name)) {
|
|
138
|
+
return;
|
|
139
|
+
}
|
|
140
|
+
|
|
141
|
+
seen.add(expr.name);
|
|
142
|
+
const { relations } = resolveFieldReference(expr.name, context);
|
|
143
|
+
|
|
144
|
+
if (!relations || relations.length === 0) {
|
|
145
|
+
return;
|
|
146
|
+
}
|
|
147
|
+
|
|
148
|
+
const last = relations[relations.length - 1]!;
|
|
149
|
+
|
|
150
|
+
// Every to-ONE relation on the path is a hop that must EXIST — including a TRAILING one,
|
|
151
|
+
// which is the case a chain of to-many relations never produces. `parent.aBool` resolves
|
|
152
|
+
// to a single to-one relation, and `NOT { parent: { is: { aBool: true } } }` is TRUE for
|
|
153
|
+
// a row with no parent at all, so the negation returns rows the PDP denies. A trailing
|
|
154
|
+
// to-MANY relation is the collection being iterated rather than a hop, and keeps its
|
|
155
|
+
// empty-collection semantics (`!tags.exists(...)` over zero tags is still TRUE).
|
|
156
|
+
const hopFilter = buildHopsExistFilter(
|
|
157
|
+
last.type === "one" ? relations : relations.slice(0, -1)
|
|
158
|
+
);
|
|
159
|
+
|
|
160
|
+
if (hopFilter !== undefined) {
|
|
161
|
+
hops.push(hopFilter);
|
|
162
|
+
}
|
|
163
|
+
|
|
164
|
+
return;
|
|
165
|
+
}
|
|
166
|
+
|
|
167
|
+
if (!isOperatorOperand(expr) || expr.operator === "lambda") {
|
|
168
|
+
return;
|
|
169
|
+
}
|
|
170
|
+
|
|
171
|
+
expr.operands.forEach(visit);
|
|
172
|
+
};
|
|
173
|
+
|
|
174
|
+
visit(operand);
|
|
175
|
+
|
|
176
|
+
if (hops.length === 0) {
|
|
177
|
+
return undefined;
|
|
178
|
+
}
|
|
179
|
+
|
|
180
|
+
return hops.length === 1 ? hops[0]! : { AND: hops };
|
|
181
|
+
}
|
|
182
|
+
|
|
183
|
+
/**
|
|
184
|
+
* `{ NOT: filter }` for a filter translated from a plan sub-expression.
|
|
185
|
+
*
|
|
186
|
+
* An unconditional filter (`{}`, which the hierarchy operators return for a relationship that
|
|
187
|
+
* holds between two constants) cannot be negated this way: Prisma drops an empty condition, so
|
|
188
|
+
* `{ NOT: {} }` is true and matches every row, where the negation of an unconditional condition
|
|
189
|
+
* matches none. Prisma has no field-free false condition to emit instead, so the shape is refused.
|
|
190
|
+
*/
|
|
191
|
+
export function negateFilter(filter: PrismaFilter): PrismaFilter {
|
|
192
|
+
if (Object.keys(filter).length === 0) {
|
|
193
|
+
throw new UnsupportedQueryPlanError(
|
|
194
|
+
"Cannot negate an unconditional filter: Prisma evaluates { NOT: {} } as true and matches " +
|
|
195
|
+
"every row, where the negated condition matches none"
|
|
196
|
+
);
|
|
197
|
+
}
|
|
198
|
+
|
|
199
|
+
return { NOT: filter };
|
|
200
|
+
}
|
|
201
|
+
|
|
202
|
+
/**
|
|
203
|
+
* `{ NOT: filter }`, with every to-one hop `operand` dots through required OUTSIDE the
|
|
204
|
+
* negation so an absent parent stays denied (see buildExpressionHopsExistFilter).
|
|
205
|
+
*/
|
|
206
|
+
export function negateRequiringHops(
|
|
207
|
+
operand: PlanExpressionOperand,
|
|
208
|
+
filter: PrismaFilter,
|
|
209
|
+
context: TranslationContext
|
|
210
|
+
): PrismaFilter {
|
|
211
|
+
const hops = buildExpressionHopsExistFilter(operand, context);
|
|
212
|
+
const negated = negateFilter(filter);
|
|
213
|
+
|
|
214
|
+
return hops === undefined ? negated : { AND: [hops, negated] };
|
|
215
|
+
}
|
|
216
|
+
|
|
217
|
+
/**
|
|
218
|
+
* Whether negating `expr` needs a hop requirement (see buildExpressionHopsExistFilter): it dots
|
|
219
|
+
* through at least one to-one hop, which is absent on a parentless row.
|
|
220
|
+
*/
|
|
221
|
+
export function referencesRequiredHop(
|
|
222
|
+
expr: PlanExpressionOperand,
|
|
223
|
+
context: TranslationContext
|
|
224
|
+
): boolean {
|
|
225
|
+
return buildExpressionHopsExistFilter(expr, context) !== undefined;
|
|
226
|
+
}
|