chore: add add_plan operation in migrations
This commit is contained in:
@@ -1,10 +1,15 @@
|
||||
import type { CustomerFilter } from "../../../filters/customerFilter.js";
|
||||
import type { PlanFilter } from "../../../filters/planFilter.js";
|
||||
import type { IRNav } from "../../ir/irTypes.js";
|
||||
import type { IRNav, IRNode, Quantifier } from "../../ir/irTypes.js";
|
||||
import { isQuantifierWrapper } from "../helpers/isQuantifierWrapper.js";
|
||||
import type { ResolutionContext } from "../resolutionContext.js";
|
||||
import { parsePlanFilter } from "../scopes/parsePlanFilter.js";
|
||||
|
||||
const QUANTIFIER_KEYS: Record<string, Quantifier> = {
|
||||
$some: "some",
|
||||
$none: "none",
|
||||
};
|
||||
|
||||
export function parsePlanNav({
|
||||
raw,
|
||||
ctx,
|
||||
@@ -12,13 +17,30 @@ export function parsePlanNav({
|
||||
raw: NonNullable<CustomerFilter["plan"]>;
|
||||
ctx: ResolutionContext;
|
||||
}): IRNav {
|
||||
// Phase 1: only $some (implicit if bare). $every / $none deferred.
|
||||
const planFilter = isQuantifierWrapper(raw) ? raw.$some : (raw as PlanFilter);
|
||||
if (!planFilter) throw new Error("plan: only $some is supported in phase 1");
|
||||
return {
|
||||
kind: "nav",
|
||||
name: "plan",
|
||||
quantifier: "some",
|
||||
child: parsePlanFilter({ filter: planFilter, ctx }),
|
||||
};
|
||||
if (!isQuantifierWrapper(raw))
|
||||
return buildNav({ quantifier: "some", filter: raw as PlanFilter, ctx });
|
||||
|
||||
for (const [key, quantifier] of Object.entries(QUANTIFIER_KEYS)) {
|
||||
const filter = (raw as Record<string, unknown>)[key] as PlanFilter | undefined;
|
||||
if (filter !== undefined) return buildNav({ quantifier, filter, ctx });
|
||||
}
|
||||
|
||||
const unsupported = Object.keys(raw).find((k) => k.startsWith("$"));
|
||||
throw new Error(`plan: ${unsupported ?? "unknown quantifier"} is not supported yet`);
|
||||
}
|
||||
|
||||
function buildNav({
|
||||
quantifier,
|
||||
filter,
|
||||
ctx,
|
||||
}: {
|
||||
quantifier: Quantifier;
|
||||
filter: PlanFilter;
|
||||
ctx: ResolutionContext;
|
||||
}): IRNav {
|
||||
const hasFields = Object.keys(filter).length > 0;
|
||||
const child: IRNode = hasFields
|
||||
? parsePlanFilter({ filter, ctx })
|
||||
: { kind: "and", children: [] };
|
||||
return { kind: "nav", name: "plan", quantifier, child };
|
||||
}
|
||||
|
||||
@@ -38,12 +38,13 @@ export type IROr = {
|
||||
children: readonly IRNode[];
|
||||
};
|
||||
|
||||
export type Quantifier = "some" | "none";
|
||||
|
||||
export type IRNav = {
|
||||
kind: "nav";
|
||||
/** Single segment naming the nav, e.g. "plan" or "item". */
|
||||
name: string;
|
||||
/** Phase 1: only "some" is supported. */
|
||||
quantifier: "some";
|
||||
quantifier: Quantifier;
|
||||
child: IRNode;
|
||||
};
|
||||
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
import type { IRLeaf, IRNode } from "../ir/irTypes.js";
|
||||
import type { IRLeaf, IRNode, Quantifier } from "../ir/irTypes.js";
|
||||
import type {
|
||||
AmbientPredicate,
|
||||
FieldDef,
|
||||
@@ -112,6 +112,7 @@ function compileNode({
|
||||
return existsForScope({
|
||||
scope: def.scope,
|
||||
child: node.child,
|
||||
quantifier: node.quantifier,
|
||||
params,
|
||||
ambient,
|
||||
});
|
||||
@@ -120,11 +121,13 @@ function compileNode({
|
||||
function existsForScope({
|
||||
scope,
|
||||
child,
|
||||
quantifier,
|
||||
params,
|
||||
ambient,
|
||||
}: {
|
||||
scope: NavScope;
|
||||
child: IRNode;
|
||||
quantifier: Quantifier;
|
||||
params: unknown[];
|
||||
ambient: AmbientContext;
|
||||
}): string {
|
||||
@@ -139,10 +142,12 @@ function existsForScope({
|
||||
params,
|
||||
ambient,
|
||||
});
|
||||
const conditions = [scope.correlation, ...ambientPreds, childSql].join(
|
||||
" AND ",
|
||||
const conditions = [scope.correlation, ...ambientPreds, childSql].filter(
|
||||
(s) => s.length > 0 && s !== "TRUE",
|
||||
);
|
||||
return `EXISTS (SELECT 1 FROM ${scope.from} WHERE ${conditions})`;
|
||||
const whereClause = conditions.length > 0 ? conditions.join(" AND ") : "TRUE";
|
||||
const keyword = quantifier === "none" ? "NOT EXISTS" : "EXISTS";
|
||||
return `${keyword} (SELECT 1 FROM ${scope.from} WHERE ${whereClause})`;
|
||||
}
|
||||
|
||||
function compileLeaf({
|
||||
|
||||
Reference in New Issue
Block a user