joist-core 2.3.0-next.63 → 2.3.0-next.64
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/build/Aliases.cjs +11 -10
- package/build/Aliases.cjs.map +1 -1
- package/build/Aliases.d.cts +19 -1
- package/build/Aliases.d.cts.map +1 -1
- package/build/Aliases.d.mts +19 -1
- package/build/Aliases.d.mts.map +1 -1
- package/build/Aliases.js +2 -1
- package/build/Aliases.js.map +1 -1
- package/build/ConditionBuilder.cjs +2 -3
- package/build/ConditionBuilder.cjs.map +1 -1
- package/build/ConditionBuilder.js +1 -2
- package/build/ConditionBuilder.js.map +1 -1
- package/build/DeferredAlias.cjs +2 -9
- package/build/DeferredAlias.cjs.map +1 -1
- package/build/DeferredAlias.d.cts +3 -5
- package/build/DeferredAlias.d.cts.map +1 -1
- package/build/DeferredAlias.d.mts +3 -5
- package/build/DeferredAlias.d.mts.map +1 -1
- package/build/DeferredAlias.js +3 -9
- package/build/DeferredAlias.js.map +1 -1
- package/build/Expr.cjs +6 -20
- package/build/Expr.cjs.map +1 -1
- package/build/Expr.d.cts +7 -9
- package/build/Expr.d.cts.map +1 -1
- package/build/Expr.d.mts +7 -9
- package/build/Expr.d.mts.map +1 -1
- package/build/Expr.js +5 -15
- package/build/Expr.js.map +1 -1
- package/build/QueryParser.cjs +1 -1
- package/build/QueryParser.cjs.map +1 -1
- package/build/QueryParser.js +1 -1
- package/build/QueryParser.js.map +1 -1
- package/build/Tables.cjs +68 -159
- package/build/Tables.cjs.map +1 -1
- package/build/Tables.d.cts +17 -0
- package/build/Tables.d.cts.map +1 -1
- package/build/Tables.d.mts +17 -0
- package/build/Tables.d.mts.map +1 -1
- package/build/Tables.js +49 -140
- package/build/Tables.js.map +1 -1
- package/build/drivers/buildUtils.cjs +5 -1
- package/build/drivers/buildUtils.cjs.map +1 -1
- package/build/drivers/buildUtils.d.cts +4 -2
- package/build/drivers/buildUtils.d.cts.map +1 -1
- package/build/drivers/buildUtils.d.mts +4 -2
- package/build/drivers/buildUtils.d.mts.map +1 -1
- package/build/drivers/buildUtils.js +5 -2
- package/build/drivers/buildUtils.js.map +1 -1
- package/build/drivers/index.cjs +1 -0
- package/build/drivers/index.d.cts +2 -2
- package/build/drivers/index.d.mts +2 -2
- package/build/drivers/index.js +2 -2
- package/build/index.cjs +3 -2
- package/build/index.cjs.map +1 -1
- package/build/index.d.cts +3 -3
- package/build/index.d.cts.map +1 -1
- package/build/index.d.mts +3 -3
- package/build/index.d.mts.map +1 -1
- package/build/index.js +3 -3
- package/build/index.js.map +1 -1
- package/build/query.cjs +7 -7
- package/build/query.cjs.map +1 -1
- package/build/query.d.cts +1 -1
- package/build/query.d.mts +1 -1
- package/build/query.js +6 -6
- package/build/query.js.map +1 -1
- package/build/skipCondition.cjs +15 -0
- package/build/skipCondition.cjs.map +1 -0
- package/build/skipCondition.d.cts +7 -0
- package/build/skipCondition.d.cts.map +1 -0
- package/build/skipCondition.d.mts +7 -0
- package/build/skipCondition.d.mts.map +1 -0
- package/build/skipCondition.js +14 -0
- package/build/skipCondition.js.map +1 -0
- package/package.json +2 -2
|
@@ -1,10 +1,8 @@
|
|
|
1
1
|
import { EntityMetadata } from "./EntityMetadata.mjs";
|
|
2
2
|
import { ColumnCondition, RawCondition } from "./QueryParser.mjs";
|
|
3
3
|
//#region src/DeferredAlias.d.ts
|
|
4
|
-
/** An undefined predicate is pruned by either query parser. */
|
|
5
|
-
declare const skipCondition: ColumnCondition;
|
|
6
4
|
declare const deferredAliasSym: unique symbol;
|
|
7
|
-
/** Resolves a domain alias
|
|
5
|
+
/** Resolves a domain alias to this em.find parse's binding. */
|
|
8
6
|
type AliasResolver = (handle: {
|
|
9
7
|
meta: EntityMetadata;
|
|
10
8
|
tableName: string;
|
|
@@ -12,7 +10,7 @@ type AliasResolver = (handle: {
|
|
|
12
10
|
meta: EntityMetadata;
|
|
13
11
|
alias: string;
|
|
14
12
|
};
|
|
15
|
-
/** A condition whose SQL aliases are resolved afresh for each occurrence in
|
|
13
|
+
/** A domain condition whose SQL aliases are resolved afresh for each occurrence in em.find. */
|
|
16
14
|
interface DeferredAliasCondition<C = ColumnCondition | RawCondition> {
|
|
17
15
|
[deferredAliasSym]: (resolve: AliasResolver) => C;
|
|
18
16
|
}
|
|
@@ -21,5 +19,5 @@ declare function isDeferredAliasCondition<C>(cond: C): cond is C & DeferredAlias
|
|
|
21
19
|
/** Tags a condition without changing its enumerable shape; resolution only mutates a fresh copy. */
|
|
22
20
|
declare function withDeferredAlias<C extends object>(cond: C, resolve: (r: AliasResolver, copy: C) => void): C & DeferredAliasCondition<C>;
|
|
23
21
|
//#endregion
|
|
24
|
-
export { AliasResolver, DeferredAliasCondition, deferredAliasSym, isDeferredAliasCondition,
|
|
22
|
+
export { AliasResolver, DeferredAliasCondition, deferredAliasSym, isDeferredAliasCondition, withDeferredAlias };
|
|
25
23
|
//# sourceMappingURL=DeferredAlias.d.mts.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"DeferredAlias.d.mts","names":[],"sources":["../src/DeferredAlias.ts"],"mappings":"
|
|
1
|
+
{"version":3,"file":"DeferredAlias.d.mts","names":[],"sources":["../src/DeferredAlias.ts"],"mappings":";;;cAIa;;KAGD,iBAAiB;EAAU,MAAM;EAAgB;;EAC3D,MAAM;EACN;;;UAIe,uBAAuB,IAAI,kBAAkB;GAC3D,oBAAoB,SAAS,kBAAkB;;;iBAIlC,yBAAyB,GAAG,MAAM,IAAI,QAAQ,IAAI,uBAAuB;;iBAKzE,kBAAkB,kBAChC,MAAM,GACN,UAAU,GAAG,eAAe,MAAM,aACjC,IAAI,uBAAuB"}
|
package/build/DeferredAlias.js
CHANGED
|
@@ -1,12 +1,6 @@
|
|
|
1
|
+
import "./EntityMetadata.js";
|
|
2
|
+
import "./QueryParser.js";
|
|
1
3
|
//#region src/DeferredAlias.ts
|
|
2
|
-
/** An undefined predicate is pruned by either query parser. */
|
|
3
|
-
const skipCondition = {
|
|
4
|
-
kind: "column",
|
|
5
|
-
alias: "skip",
|
|
6
|
-
column: "skip",
|
|
7
|
-
dbType: "skip",
|
|
8
|
-
cond: void 0
|
|
9
|
-
};
|
|
10
4
|
const deferredAliasSym = Symbol("joist.deferredAliasCondition");
|
|
11
5
|
/** Detects conditions that carry a per-parse alias resolver. */
|
|
12
6
|
function isDeferredAliasCondition(cond) {
|
|
@@ -24,6 +18,6 @@ function withDeferredAlias(cond, resolve) {
|
|
|
24
18
|
});
|
|
25
19
|
}
|
|
26
20
|
//#endregion
|
|
27
|
-
export { deferredAliasSym, isDeferredAliasCondition,
|
|
21
|
+
export { deferredAliasSym, isDeferredAliasCondition, withDeferredAlias };
|
|
28
22
|
|
|
29
23
|
//# sourceMappingURL=DeferredAlias.js.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"DeferredAlias.js","names":[],"sources":["../src/DeferredAlias.ts"],"sourcesContent":["// Erased imports keep domain predicates
|
|
1
|
+
{"version":3,"file":"DeferredAlias.js","names":[],"sources":["../src/DeferredAlias.ts"],"sourcesContent":["// Erased imports keep domain predicates independent of the query parser at module load time.\nimport { type EntityMetadata } from \"./EntityMetadata.ts\";\nimport { type ColumnCondition, type RawCondition } from \"./QueryParser.ts\";\n\nexport const deferredAliasSym: unique symbol = Symbol(\"joist.deferredAliasCondition\");\n\n/** Resolves a domain alias to this em.find parse's binding. */\nexport type AliasResolver = (handle: { meta: EntityMetadata; tableName: string }) => {\n meta: EntityMetadata;\n alias: string;\n};\n\n/** A domain condition whose SQL aliases are resolved afresh for each occurrence in em.find. */\nexport interface DeferredAliasCondition<C = ColumnCondition | RawCondition> {\n [deferredAliasSym]: (resolve: AliasResolver) => C;\n}\n\n/** Detects conditions that carry a per-parse alias resolver. */\nexport function isDeferredAliasCondition<C>(cond: C): cond is C & DeferredAliasCondition<C> {\n return typeof cond === \"object\" && cond !== null && deferredAliasSym in cond;\n}\n\n/** Tags a condition without changing its enumerable shape; resolution only mutates a fresh copy. */\nexport function withDeferredAlias<C extends object>(\n cond: C,\n resolve: (r: AliasResolver, copy: C) => void,\n): C & DeferredAliasCondition<C> {\n return Object.defineProperty(cond, deferredAliasSym, {\n value: (r: AliasResolver) => {\n const copy = { ...cond };\n resolve(r, copy);\n return copy;\n },\n enumerable: false,\n }) as C & DeferredAliasCondition<C>;\n}\n"],"mappings":";;;AAIA,MAAa,mBAAkC,OAAO,8BAA8B;;AAcpF,SAAgB,yBAA4B,MAAgD;CAC1F,OAAO,OAAO,SAAS,YAAY,SAAS,QAAQ,oBAAoB;AAC1E;;AAGA,SAAgB,kBACd,MACA,SAC+B;CAC/B,OAAO,OAAO,eAAe,MAAM,kBAAkB;EACnD,QAAQ,MAAqB;GAC3B,MAAM,OAAO,EAAE,GAAG,KAAK;GACvB,QAAQ,GAAG,IAAI;GACf,OAAO;EACT;EACA,YAAY;CACd,CAAC;AACH"}
|
package/build/Expr.cjs
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
Object.defineProperty(exports, Symbol.toStringTag, { value: "Module" });
|
|
2
2
|
const require_keywords = require("./keywords.cjs");
|
|
3
|
-
const
|
|
3
|
+
const require_skipCondition = require("./skipCondition.cjs");
|
|
4
4
|
//#region src/Expr.ts
|
|
5
5
|
/**
|
|
6
6
|
* The shared expression protocol for `em.query`.
|
|
@@ -52,7 +52,6 @@ function deferredCondition(fn) {
|
|
|
52
52
|
function resolveDeferredConditions(cond, ctx) {
|
|
53
53
|
if (cond === void 0 || cond === null) return cond;
|
|
54
54
|
if (isDeferredCondition(cond)) return cond[deferredSym](ctx);
|
|
55
|
-
else if (require_DeferredAlias.isDeferredAliasCondition(cond)) return cond[require_DeferredAlias.deferredAliasSym](ctxResolver(ctx));
|
|
56
55
|
else if ("and" in cond && cond.and) return {
|
|
57
56
|
...cond,
|
|
58
57
|
and: cond.and.map((c) => resolveDeferredConditions(c, ctx))
|
|
@@ -74,8 +73,8 @@ function joinFragments(parts, sep) {
|
|
|
74
73
|
/**
|
|
75
74
|
* The methods every expression shares. Subclasses provide `toSql`, and usually `decode`/`encode`.
|
|
76
75
|
*
|
|
77
|
-
*
|
|
78
|
-
*
|
|
76
|
+
* Table columns can override comparisons to apply column-specific conversions. Domain aliases do not
|
|
77
|
+
* implement this protocol.
|
|
79
78
|
*/
|
|
80
79
|
var BaseExpr = class {
|
|
81
80
|
[exprBrand] = this;
|
|
@@ -196,7 +195,7 @@ var BaseExpr = class {
|
|
|
196
195
|
}
|
|
197
196
|
/** `this op value`, where `value` may be `undefined` (pruned), `null`, another expression, or a literal. */
|
|
198
197
|
compare(op, value) {
|
|
199
|
-
if (value === void 0) return
|
|
198
|
+
if (value === void 0) return require_skipCondition.skipCondition;
|
|
200
199
|
if (value === null) {
|
|
201
200
|
const not = op === "=" ? "" : op === "!=" ? "NOT " : fail(`Cannot compare ${op} to null`);
|
|
202
201
|
return deferredCondition((ctx) => {
|
|
@@ -219,7 +218,7 @@ var BaseExpr = class {
|
|
|
219
218
|
}
|
|
220
219
|
/** `this IN (subquery)` or `this = ANY(?)` for a list; `NOT IN` / `!= ALL(?)` for `nin`. */
|
|
221
220
|
inList(op, values) {
|
|
222
|
-
if (values === void 0) return
|
|
221
|
+
if (values === void 0) return require_skipCondition.skipCondition;
|
|
223
222
|
if (isExpr(values)) return deferredCondition((ctx) => {
|
|
224
223
|
const left = this.toSql(ctx);
|
|
225
224
|
const right = asNode(values).toSqlBare(ctx);
|
|
@@ -330,7 +329,7 @@ var RefExpr = class extends BaseExpr {
|
|
|
330
329
|
/**
|
|
331
330
|
* A `sql` tagged template.
|
|
332
331
|
*
|
|
333
|
-
* For an Author
|
|
332
|
+
* For an Author table `a` assigned the SQL alias `a1`:
|
|
334
333
|
*
|
|
335
334
|
* ```ts
|
|
336
335
|
* sql`${a.age} * 2` // Expression: a1.age * 2
|
|
@@ -512,15 +511,6 @@ function minMaxOutputType(outputType) {
|
|
|
512
511
|
default: return;
|
|
513
512
|
}
|
|
514
513
|
}
|
|
515
|
-
/** An `AliasResolver` backed by an `ExprContext`; a handle's bound meta is its own (`em.query` sources are their own tables). */
|
|
516
|
-
function ctxResolver(ctx) {
|
|
517
|
-
return function resolve(handle) {
|
|
518
|
-
return {
|
|
519
|
-
meta: handle.meta,
|
|
520
|
-
alias: ctx.aliasFor(handle)
|
|
521
|
-
};
|
|
522
|
-
};
|
|
523
|
-
}
|
|
524
514
|
/** Decodes `count`/`sum`/`avg` results, which Postgres returns as strings for bigint/numeric. */
|
|
525
515
|
function decodeNumber(value) {
|
|
526
516
|
return typeof value === "string" ? Number(value) : value;
|
|
@@ -540,18 +530,14 @@ exports.TemplateExpr = TemplateExpr;
|
|
|
540
530
|
exports.arrayOutputType = arrayOutputType;
|
|
541
531
|
exports.asNode = asNode;
|
|
542
532
|
exports.canonicalDbType = canonicalDbType;
|
|
543
|
-
exports.deferredAliasSym = require_DeferredAlias.deferredAliasSym;
|
|
544
533
|
exports.deferredCondition = deferredCondition;
|
|
545
534
|
exports.deferredSym = deferredSym;
|
|
546
535
|
exports.exprBrand = exprBrand;
|
|
547
536
|
exports.interpolationToSql = interpolationToSql;
|
|
548
537
|
exports.isConditionLike = isConditionLike;
|
|
549
|
-
exports.isDeferredAliasCondition = require_DeferredAlias.isDeferredAliasCondition;
|
|
550
538
|
exports.isDeferredCondition = isDeferredCondition;
|
|
551
539
|
exports.isExpr = isExpr;
|
|
552
540
|
exports.joinFragments = joinFragments;
|
|
553
541
|
exports.resolveDeferredConditions = resolveDeferredConditions;
|
|
554
|
-
exports.skipCondition = require_DeferredAlias.skipCondition;
|
|
555
|
-
exports.withDeferredAlias = require_DeferredAlias.withDeferredAlias;
|
|
556
542
|
|
|
557
543
|
//# sourceMappingURL=Expr.cjs.map
|
package/build/Expr.cjs.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"Expr.cjs","names":["isDeferredAliasCondition","deferredAliasSym","skipCondition","safeKq"],"sources":["../src/Expr.ts"],"sourcesContent":["import { type AliasResolver, deferredAliasSym, isDeferredAliasCondition, skipCondition } from \"./DeferredAlias.ts\";\nexport {\n type AliasResolver,\n type DeferredAliasCondition,\n deferredAliasSym,\n isDeferredAliasCondition,\n skipCondition,\n withDeferredAlias,\n} from \"./DeferredAlias.ts\";\nimport type { ExpressionCondition } from \"./EntityFilter.ts\";\nimport type { EntityMetadata } from \"./EntityMetadata.ts\";\nimport { safeKq } from \"./keywords.ts\";\nimport type { RawCondition } from \"./QueryParser.ts\";\n\n/**\n * The shared expression protocol for `em.query`.\n *\n * Table columns (`a.first_name`), aggregates (`b.id.count()`), `sql` templates, subquery columns\n * (`bookStats.bookCount`), and scalar subqueries all implement it, so any of them can appear in\n * `select`, `where`, `groupBy`, `having`, `orderBy`, and inside other expressions.\n *\n * This module is a leaf on purpose: `Tables.ts` extends `BaseExpr` at load time, so nothing here may\n * import (at runtime) a module that leads back to `Tables.ts`. Anything that needs metadata, alias\n * binding, or SQL generation for conditions is reached through the `ExprContext` the query parser passes in.\n */\n\nexport const exprBrand: unique symbol = Symbol(\"joist.expr\");\n\n/**\n * Phantom type information carried by every `Expr`.\n *\n * `R` is the decoded result type.\n *\n * `Src` is the expression's *source key*: the type-level identity of the table it reads from. An entity\n * table's key is its type name (`table(Author)` gives `\"Author\"`) or the explicit name in\n * `table(Author, \"m\")`; a subquery's key is its `as: \"book_stats\"`, or the shared sentinel `\"?\"` when it\n * has no `as`. Exactly two questions are asked of a source key, and nothing else:\n *\n * - `MaybeNull` asks \"is my source key among the LEFT-joined sources in this query's join list?\" If yes,\n * the value can be `null`.\n * - `CheckScope` asks \"is my source key among `from` + `join` at all?\" If no, the query reads from a\n * table it never joined.\n *\n * Two special keys opt out of both questions:\n *\n * - `Expr<number, never>` reads from nothing that can be left-joined away, i.e. `b.id.count()`\n * - `Expr<number, string>` (the default) is untracked, i.e. `sql.ref` on an unknown table\n */\nexport interface ExprBrand<R, Src extends string> {\n readonly __result: R;\n readonly __source: Src;\n}\n\n/**\n * Runtime type information that lets set operations reject outputs that cannot safely share conversions.\n *\n * PostgreSQL compares projected SQL rows and chooses a common SQL type before Joist decodes them.\n * A compound uses one decoder per output column and retains an encoder for later comparisons and\n * coalesce fallbacks. We must check every operand before reusing the first operand's conversions:\n * TypeScript result types are erased, and equal SQL storage types do not imply equal logical domains.\n *\n * I.e. Author.id and Book.author_id are compatible Author IDs despite having separate serdes, while\n * Book.id must not compare or decode as an Author ID just because both use int4. Conversely, Author.age\n * and Author.age.sum() both expose TypeScript numbers but produce int4 and int8 driver values. Without\n * their SQL type information, accepting them could make decoding depend on which operand comes first.\n * Enum, custom-mapper, and JSON-schema domain tokens also prevent unrelated conversions from mixing.\n *\n * Compatibility requires exact equality of the canonical SQL `dbType`, the `domain` token, and\n * `idMeta`. An absent descriptor is unknown, not an identity codec. `idMeta` identifies scalar IDs only;\n * arrays retain their element domain separately so they cannot select a polymorphic IN component.\n * Raw `sql<R>` annotations do not supply this runtime information. Encoding and decoding remain on the\n * expression; this descriptor only holds the information needed for these conservative checks.\n */\nexport interface ExprOutputType {\n dbType: string;\n domain: unknown;\n idMeta?: EntityMetadata;\n /** Internal conversion proof, consistent for equal dbType/domain/idMeta; not another compatibility key. */\n arrayElementSafe?: boolean;\n}\n\n/**\n * \"Any expression whose result is `R`\", checked by brand alone.\n *\n * Method parameters use this instead of `Expr<R>` so that `Expr` stays covariant in `R`: checking\n * `Expr<AuthorId>` against `Expr<AuthorId | null>` then only compares the phantom `__result`, not every\n * method's parameter list (which would make `Expr` invariant, and break `select: b.author_id` dispatch and\n * polymorphic joins).\n */\nexport type ExprLike<R> = { readonly [exprBrand]: ExprBrand<R, any> };\n\n/**\n * A typed SQL expression: a table column, an aggregate, a `sql` template, or a scalar subquery.\n *\n * Conditions and SQL functions are methods, so they need no import. Aggregates keep `Src` so scope\n * checking still sees through `bs.x.max()`; `count` is source-less because `count(x)` is 0, not null,\n * when `x`'s table is left-joined away; `coalesce` drops `Src` on purpose, since its whole job is to\n * remove the nullability a left join adds.\n */\nexport interface Expr<R, Src extends string = string> {\n readonly [exprBrand]: ExprBrand<R, Src>;\n eq(value: R | ExprLike<R> | undefined): ExpressionCondition;\n ne(value: R | ExprLike<R> | undefined): ExpressionCondition;\n gt(value: R | ExprLike<R> | undefined): ExpressionCondition;\n gte(value: R | ExprLike<R> | undefined): ExpressionCondition;\n lt(value: R | ExprLike<R> | undefined): ExpressionCondition;\n lte(value: R | ExprLike<R> | undefined): ExpressionCondition;\n // A list subquery may select a nullable column; NULLs in the set never match, so that is fine.\n in(values: readonly R[] | ExprLike<R | null> | undefined): ExpressionCondition;\n nin(values: readonly R[] | ExprLike<R | null> | undefined): ExpressionCondition;\n\n /** `count(x)::int`; `count(a.id)` is `count(*)` for the FROM table, and the matched-row count for a left-joined one. */\n count(): Expr<number, never>;\n countDistinct(): Expr<number, never>;\n sum(this: Expr<number | null, Src>): Expr<number | null, Src>;\n avg(this: Expr<number | null, Src>): Expr<number | null, Src>;\n min(): Expr<R | null, Src>;\n max(): Expr<R | null, Src>;\n /** PG keeps element NULLs (a left-joined empty group aggregates as `[null]`), and zero rows aggregate as NULL. */\n arrayAgg(): Expr<R[] | null, Src>;\n stringAgg(this: Expr<string | null, Src>, delimiter: string): Expr<string | null, Src>;\n coalesce(fallback: NonNullable<R>): Expr<NonNullable<R>, never>;\n}\n\n/**\n * A join entry: the join kind is the key, the joined source is the value, plus `on`. `inner?: never` /\n * `left?: never` keep an entry to one kind (the `ExpressionFilter` `and`/`or` trick).\n *\n * `on` is required. A join is pruned when nothing references it anymore, not by an `undefined` ON;\n * `keep: true` pins a join that would otherwise prune, i.e. an inner join used as an existence filter,\n * the way em.find's `keepAliases` does. It is a boolean so callers can pass a flag.\n *\n * Declared here (not `query.ts`) so the relation join factories in `Tables.ts` (i.e. `a.books.as(b)`) can\n * return them without importing `query.ts`; `query.ts` re-constrains `A` to its `QuerySource`.\n */\nexport interface InnerJoin<A> {\n readonly inner: A;\n readonly left?: never;\n readonly on: ExpressionCondition;\n readonly keep?: boolean;\n}\n\nexport interface LeftJoin<A> {\n readonly left: A;\n readonly inner?: never;\n readonly on: ExpressionCondition;\n readonly keep?: boolean;\n}\n\n/** SQL plus its `?` bindings plus the SQL aliases it references, i.e. for join pruning. */\nexport interface SqlFragment {\n sql: string;\n bindings: any[];\n refs: string[];\n}\n\n/**\n * What an expression needs from the query it is generating SQL for: the SQL alias assigned to each\n * source, and a way to turn nested conditions into SQL (which needs `ConditionBuilder`, so it lives in `query.ts`).\n */\nexport interface ExprContext {\n /** Returns the SQL alias for an alias's `AliasMgmt` or a subquery handle, searching enclosing queries. */\n aliasFor(handle: object): string;\n /** Turns a user-facing condition into SQL; `undefined` if it pruned away entirely. */\n conditionToSql(cond: ExpressionCondition): SqlFragment | undefined;\n}\n\nexport function isExpr(value: unknown): value is ExprLike<any> {\n return typeof value === \"object\" && value !== null && exprBrand in value;\n}\n\n/** Every `Expr` is a `BaseExpr` at runtime; this cast keeps `isExpr` a plain type guard so unions narrow. */\nexport function asNode(expr: ExprLike<any>): BaseExpr {\n return expr as any as BaseExpr;\n}\n\nexport const deferredSym: unique symbol = Symbol(\"joist.deferredCondition\");\n\n/**\n * A condition whose SQL depends on aliases that are only known once the query is parsed, i.e.\n * `bookStats.bookCount.gt(1)` or `bs.authorId.eq(a.id)`.\n *\n * It is shaped like a `RawCondition` so it can sit in any `ExpressionFilter`; `resolveDeferredConditions`\n * snapshots `condition`, `bindings`, and `aliases` before the filter is parsed. Alias columns compared to\n * literals keep producing `ColumnCondition`s (the `em.find` path), so this is only for comparisons that\n * involve a non-alias expression.\n */\nexport interface DeferredCondition extends RawCondition {\n [deferredSym]: (ctx: ExprContext) => RawCondition;\n}\n\nexport function isDeferredCondition(cond: unknown): cond is DeferredCondition {\n return typeof cond === \"object\" && cond !== null && deferredSym in cond;\n}\n\n/** Creates a `DeferredCondition` that generates its SQL with `fn` once the query's aliases are known. */\nexport function deferredCondition(fn: (ctx: ExprContext) => SqlFragment): DeferredCondition {\n const cond: DeferredCondition = {\n kind: \"raw\",\n aliases: [],\n condition: \"<unresolved>\",\n bindings: [],\n pruneable: false,\n [deferredSym]: (ctx) => {\n const { sql, bindings, refs } = fn(ctx);\n return { ...cond, condition: sql, bindings, aliases: refs };\n },\n };\n return cond;\n}\n\n/**\n * Resolve each condition occurrence and snapshot it before resolving the next one. A nested subquery\n * may reuse the same condition under another alias; it must not overwrite this occurrence's SQL.\n */\nexport function resolveDeferredConditions(\n cond: ExpressionCondition | undefined,\n ctx: ExprContext,\n): ExpressionCondition | undefined {\n if (cond === undefined || cond === null) return cond;\n if (isDeferredCondition(cond)) {\n return cond[deferredSym](ctx);\n } else if (isDeferredAliasCondition(cond)) {\n return cond[deferredAliasSym](ctxResolver(ctx));\n } else if (\"and\" in cond && cond.and) {\n return { ...cond, and: cond.and.map((c) => resolveDeferredConditions(c, ctx)) };\n } else if (\"or\" in cond && cond.or) {\n return { ...cond, or: cond.or.map((c) => resolveDeferredConditions(c, ctx)) };\n }\n return cond;\n}\n\n/** Concatenates SQL fragments with `sep`, keeping bindings and refs in order. */\nexport function joinFragments(parts: SqlFragment[], sep: string): SqlFragment {\n return {\n sql: parts.map((p) => p.sql).join(sep),\n bindings: parts.flatMap((p) => p.bindings),\n refs: parts.flatMap((p) => p.refs),\n };\n}\n\n/**\n * The methods every expression shares. Subclasses provide `toSql`, and usually `decode`/`encode`.\n *\n * Alias columns override the comparison methods with the `em.find` `ColumnCondition` path when the\n * right-hand side is a literal, and fall back to these for expression-vs-expression comparisons.\n */\nexport abstract class BaseExpr {\n readonly [exprBrand]: any = this;\n\n /** Produces this expression's SQL so it can be embedded in a larger expression, i.e. a subquery gets parens. */\n abstract toSql(ctx: ExprContext): SqlFragment;\n\n /** Only an actual scalar/IN subquery exposes its selected expression, not an ordinary ID expression. */\n get subquerySelect(): BaseExpr | undefined {\n return undefined;\n }\n\n /** Known SQL representation and logical domain; raw SQL and unmodeled refs remain unknown. */\n get outputType(): ExprOutputType | undefined {\n return undefined;\n }\n\n /** Physical SQL nullability; undefined means unknown, not a NOT NULL guarantee. */\n get sqlNullable(): boolean | undefined {\n return undefined;\n }\n\n /** The source of a direct column reference, whose value becomes NULL under an unmatched LEFT join. */\n get sqlSource(): object | undefined {\n return undefined;\n }\n\n /** Produces the SQL without the outer parens a subquery normally gets; only differs for subqueries. */\n toSqlBare(ctx: ExprContext): SqlFragment {\n return this.toSql(ctx);\n }\n\n /** Converts a result-set value into the domain value, i.e. an int into a tagged id. */\n decode(value: unknown): unknown {\n return value;\n }\n\n /** Converts a domain value into the database value, i.e. a tagged id into an int, for bindings. */\n encode(value: unknown): unknown {\n return value;\n }\n\n eq(value: unknown): ExpressionCondition {\n return this.compare(\"=\", value);\n }\n\n ne(value: unknown): ExpressionCondition {\n return this.compare(\"!=\", value);\n }\n\n gt(value: unknown): ExpressionCondition {\n return this.compare(\">\", value);\n }\n\n gte(value: unknown): ExpressionCondition {\n return this.compare(\">=\", value);\n }\n\n lt(value: unknown): ExpressionCondition {\n return this.compare(\"<\", value);\n }\n\n lte(value: unknown): ExpressionCondition {\n return this.compare(\"<=\", value);\n }\n\n in(values: unknown): ExpressionCondition {\n return this.inList(\"IN\", values);\n }\n\n nin(values: unknown): ExpressionCondition {\n return this.inList(\"NOT IN\", values);\n }\n\n count(): Expr<number, never> {\n return new FnExpr(\"count\", [this], {\n suffix: \"::int\",\n decode: decodeNumber,\n encode: identity,\n outputType: { dbType: \"int4\", domain: Number, arrayElementSafe: true },\n }) as any;\n }\n\n countDistinct(): Expr<number, never> {\n return new FnExpr(\"count\", [this], {\n prefix: \"DISTINCT \",\n suffix: \"::int\",\n decode: decodeNumber,\n encode: identity,\n outputType: { dbType: \"int4\", domain: Number, arrayElementSafe: true },\n }) as any;\n }\n\n sum(): Expr<number | null, any> {\n return new FnExpr(\"sum\", [this], {\n decode: decodeNumber,\n encode: identity,\n outputType: numericAggregateOutputType(\"sum\", this.outputType),\n }) as any;\n }\n\n avg(): Expr<number | null, any> {\n return new FnExpr(\"avg\", [this], {\n decode: decodeNumber,\n encode: identity,\n outputType: numericAggregateOutputType(\"avg\", this.outputType),\n }) as any;\n }\n\n min(): Expr<any, any> {\n return new FnExpr(\"min\", [this], {\n decode: (v) => this.decode(v),\n outputType: minMaxOutputType(this.outputType),\n }) as any;\n }\n\n max(): Expr<any, any> {\n return new FnExpr(\"max\", [this], {\n decode: (v) => this.decode(v),\n outputType: minMaxOutputType(this.outputType),\n }) as any;\n }\n\n arrayAgg(): Expr<any, any> {\n // Values are arrays while the argument encodes/decodes *elements*, i.e. a `.coalesce([\"b:1\"])`\n // fallback must encode each tagged id, not hand the whole array to the id column's encoder\n return new FnExpr(\"array_agg\", [this], {\n decode: (v) => (Array.isArray(v) ? v.map((e) => this.decode(e)) : v),\n encode: (v) => (Array.isArray(v) ? v.map((e) => this.encode(e)) : v),\n outputType: arrayOutputType(this.outputType),\n }) as any;\n }\n\n stringAgg(delimiter: string): Expr<string | null, any> {\n return new FnExpr(\"string_agg\", [this, new BindingExpr(delimiter)], {\n outputType:\n this.outputType?.domain === String ? { dbType: \"text\", domain: String, arrayElementSafe: true } : undefined,\n }) as any;\n }\n\n coalesce(fallback: unknown): Expr<any, never> {\n return new FnExpr(\"coalesce\", [this, new BindingExpr(this.encode(fallback))], {\n decode: (v) => this.decode(v),\n outputType: this.outputType,\n }) as any;\n }\n\n /** `this op value`, where `value` may be `undefined` (pruned), `null`, another expression, or a literal. */\n protected compare(op: string, value: unknown): ExpressionCondition {\n if (value === undefined) return skipCondition;\n if (value === null) {\n const not = op === \"=\" ? \"\" : op === \"!=\" ? \"NOT \" : fail(`Cannot compare ${op} to null`);\n return deferredCondition((ctx) => {\n const left = this.toSql(ctx);\n return { ...left, sql: `${left.sql} IS ${not}NULL` };\n });\n }\n if (isExpr(value)) {\n return deferredCondition((ctx) => joinFragments([this.toSql(ctx), asNode(value).toSql(ctx)], ` ${op} `));\n }\n return deferredCondition((ctx) => {\n const left = this.toSql(ctx);\n return { sql: `${left.sql} ${op} ?`, bindings: [...left.bindings, this.encode(value)], refs: left.refs };\n });\n }\n\n /** `this IN (subquery)` or `this = ANY(?)` for a list; `NOT IN` / `!= ALL(?)` for `nin`. */\n protected inList(op: \"IN\" | \"NOT IN\", values: unknown): ExpressionCondition {\n if (values === undefined) return skipCondition;\n if (isExpr(values)) {\n return deferredCondition((ctx) => {\n const left = this.toSql(ctx);\n const right = asNode(values).toSqlBare(ctx);\n return joinFragments([left, { ...right, sql: `(${right.sql})` }], ` ${op} `);\n });\n }\n if (!Array.isArray(values)) fail(`Expected an array or subquery for ${op}, got ${values}`);\n const fn = op === \"IN\" ? \"= ANY(?)\" : \"!= ALL(?)\";\n return deferredCondition((ctx) => {\n const left = this.toSql(ctx);\n return {\n sql: `${left.sql} ${fn}`,\n bindings: [...left.bindings, values.map((v) => this.encode(v))],\n refs: left.refs,\n };\n });\n }\n}\n\n/**\n * A SQL function applied to expressions, i.e. `count(a.\"id\")::int` or `coalesce(bs.\"n\", ?)`.\n *\n * By default decoding is identity and encoding follows the first argument. Callers explicitly supply\n * a decoder and output type when needed (`max(a.id)` is still an id); numeric aggregates supply their\n * own decoder/encoder, since `count(a.id)` is a number, not an id. Unknown functions have no output type.\n */\nexport class FnExpr extends BaseExpr {\n constructor(\n private name: string,\n private args: BaseExpr[],\n private opts: {\n prefix?: string;\n suffix?: string;\n decode?: (value: unknown) => unknown;\n encode?: (value: unknown) => unknown;\n outputType?: ExprOutputType;\n },\n ) {\n super();\n }\n\n get outputType(): ExprOutputType | undefined {\n return this.opts.outputType;\n }\n\n get sqlNullable(): boolean | undefined {\n switch (this.name) {\n case \"count\":\n return false;\n case \"sum\":\n case \"avg\":\n case \"min\":\n case \"max\":\n case \"array_agg\":\n case \"string_agg\":\n return true;\n case \"coalesce\":\n // Only the fallback is independent of an outer query's LEFT joins.\n return this.args[1]?.sqlNullable === false ? false : undefined;\n default:\n return undefined;\n }\n }\n\n toSql(ctx: ExprContext): SqlFragment {\n const args = joinFragments(\n this.args.map((a) => a.toSql(ctx)),\n \", \",\n );\n return { ...args, sql: `${this.name}(${this.opts.prefix ?? \"\"}${args.sql})${this.opts.suffix ?? \"\"}` };\n }\n\n decode(value: unknown): unknown {\n if (value === null || value === undefined) return value;\n return this.opts.decode ? this.opts.decode(value) : value;\n }\n\n encode(value: unknown): unknown {\n return this.opts.encode ? this.opts.encode(value) : this.args[0].encode(value);\n }\n}\n\n/** A bound literal, i.e. the `?` in `coalesce(x, ?)`. */\nexport class BindingExpr extends BaseExpr {\n constructor(private value: unknown) {\n super();\n }\n\n get sqlNullable(): boolean {\n return this.value === null || this.value === undefined;\n }\n\n toSql(): SqlFragment {\n return { sql: \"?\", bindings: [this.value], refs: [] };\n }\n}\n\n/** An unmodeled column on a known source, i.e. `sql.ref(a, \"ts_search\")`; untracked at the type level. */\nexport class RefExpr extends BaseExpr {\n constructor(\n private handle: object,\n private column: string,\n ) {\n super();\n }\n\n get sqlSource(): object {\n return this.handle;\n }\n\n toSql(ctx: ExprContext): SqlFragment {\n const alias = ctx.aliasFor(this.handle);\n // safeKq for both halves: sql.ref takes user strings, and a subquery alias is its `as` name\n return { sql: `${safeKq(alias)}.${safeKq(this.column)}`, bindings: [], refs: [alias] };\n }\n}\n\n/**\n * A `sql` tagged template.\n *\n * For an Author alias `a` assigned the SQL alias `a1`:\n *\n * ```ts\n * sql`${a.age} * 2` // Expression: a1.age * 2\n * sql`${a.age.gte(18)}` // Condition: (a1.age >= ?), bindings [18]\n * sql`${\"Alice\"}` // Value: ?, bindings [\"Alice\"]\n * ```\n */\nexport class TemplateExpr extends BaseExpr {\n constructor(\n private strings: TemplateStringsArray,\n private values: unknown[],\n ) {\n super();\n }\n\n toSql(ctx: ExprContext): SqlFragment {\n const parts: SqlFragment[] = [];\n this.strings.forEach((s, i) => {\n parts.push({ sql: s, bindings: [], refs: [] });\n if (i < this.values.length) parts.push(interpolationToSql(this.values[i], ctx));\n });\n return joinFragments(parts, \"\");\n }\n}\n\n/** Turns one `${...}` of a `sql` template into SQL: an expression, a condition, or a bound value. */\nexport function interpolationToSql(value: unknown, ctx: ExprContext): SqlFragment {\n if (isExpr(value)) {\n return asNode(value).toSql(ctx);\n } else if (isConditionLike(value)) {\n return ctx.conditionToSql(value as ExpressionCondition) ?? { sql: \"true\", bindings: [], refs: [] };\n } else {\n return { sql: \"?\", bindings: [value], refs: [] };\n }\n}\n\n/** True for the user-facing condition shapes: `{ and }`, `{ or }`, `ColumnCondition`, `RawCondition`. */\nexport function isConditionLike(value: unknown): boolean {\n if (typeof value !== \"object\" || value === null) return false;\n const v = value as any;\n return \"and\" in v || \"or\" in v || v.kind === \"column\" || v.kind === \"raw\" || v.kind === \"exists\";\n}\n\n/**\n * Normalizes known PostgreSQL type synonyms without treating coercible types or user types as equal.\n * The output compatibility checks compare these names, so normalize only spellings of the same type.\n *\n * I.e. `integer` and `int` both mean `int4`; `integer[]` and `int4[]` also describe the same SQL type.\n * Keeping these spellings distinct would reject compatible outputs just because their serdes use\n * different names for the same representation.\n *\n * I.e. `int4` can be promoted to `int8`, but they are not synonyms: classic pg returns int4 as a number\n * and int8 as a string. Treating them as equal could admit a compound whose first operand's decoder\n * cannot handle the common SQL type. This function does not choose or apply a conversion.\n *\n * I.e. `citext` must stay distinct from `text`: they disagree on whether 'Alice' and 'alice' are equal,\n * which matters when a set operation compares rows. A user-defined `app.email` domain over text can\n * also impose constraints that text does not. Unknown names remain unchanged, including their schema\n * and quoting; neither coercibility nor a shared base type proves that their outputs are compatible.\n */\nexport function canonicalDbType(dbType: string): string {\n if (dbType.endsWith(\"[]\")) return `${canonicalDbType(dbType.slice(0, -2))}[]`;\n switch (dbType) {\n case \"smallint\":\n return \"int2\";\n case \"int\":\n case \"integer\":\n return \"int4\";\n case \"bigint\":\n return \"int8\";\n case \"decimal\":\n return \"numeric\";\n case \"real\":\n return \"float4\";\n case \"double precision\":\n return \"float8\";\n case \"boolean\":\n return \"bool\";\n case \"character varying\":\n return \"varchar\";\n case \"character\":\n return \"bpchar\";\n case \"timestamp without time zone\":\n return \"timestamp\";\n case \"timestamp with time zone\":\n return \"timestamptz\";\n case \"time without time zone\":\n return \"time\";\n case \"time with time zone\":\n return \"timetz\";\n default:\n return dbType;\n }\n}\n\nconst arrayDomains = new Map<unknown, Map<EntityMetadata | undefined, symbol>>();\n\n// Both classic pg and lazy binary reads decode these physical arrays, not an unparsed array string.\nconst arrayElementDbTypes = new Set([\n \"bool\",\n \"bytea\",\n \"int2\",\n \"int4\",\n \"int8\",\n \"float4\",\n \"float8\",\n \"numeric\",\n \"text\",\n \"varchar\",\n \"bpchar\",\n \"uuid\",\n \"json\",\n \"jsonb\",\n]);\n\n/**\n * Describes elementwise conversion only for known physical array parsers and stable domain conversion.\n *\n * I.e. `a.id.arrayAgg()` wraps Author's tag and `idMeta` in its domain token, but has no scalar\n * `idMeta` of its own. Repeated expressions share the token; IDs of subtypes with a shared tag do not.\n * Native enum/citext, date/time, and nested SQL arrays remain unknown. Numeric elements can be numbers\n * in classic pg and strings in lazy binary reads, so a scalar string-only mapper is not sufficient.\n */\nexport function arrayOutputType(outputType: ExprOutputType | undefined): ExprOutputType | undefined {\n if (!outputType?.arrayElementSafe || !arrayElementDbTypes.has(outputType.dbType)) return undefined;\n let domains = arrayDomains.get(outputType.domain);\n if (!domains) arrayDomains.set(outputType.domain, (domains = new Map()));\n let domain = domains.get(outputType.idMeta);\n if (!domain) domains.set(outputType.idMeta, (domain = Symbol(\"joist.arrayOutput\")));\n return { dbType: `${outputType.dbType}[]`, domain };\n}\n\n/**\n * Resolves the SQL result type of supported numeric aggregates; other overloads remain unknown.\n *\n * I.e. `a.age.sum()` has `dbType: \"int8\"` and Number conversion, while `a.age` has `dbType: \"int4\"`\n * and identity conversion. Their number domains agree, but SQL types reject a union in either order.\n */\nfunction numericAggregateOutputType(\n name: \"sum\" | \"avg\",\n outputType: ExprOutputType | undefined,\n): ExprOutputType | undefined {\n switch (outputType?.dbType) {\n case \"int2\":\n case \"int4\":\n return { dbType: name === \"sum\" ? \"int8\" : \"numeric\", domain: Number, arrayElementSafe: true };\n case \"int8\":\n case \"numeric\":\n return { dbType: \"numeric\", domain: Number, arrayElementSafe: true };\n case \"float4\":\n return { dbType: name === \"sum\" ? \"float4\" : \"float8\", domain: Number, arrayElementSafe: true };\n case \"float8\":\n return { dbType: \"float8\", domain: Number, arrayElementSafe: true };\n default:\n return undefined;\n }\n}\n\n/** Only known MIN/MAX overloads have predictable output types; varchar/name use the text overload. */\nfunction minMaxOutputType(outputType: ExprOutputType | undefined): ExprOutputType | undefined {\n switch (outputType?.dbType) {\n case \"varchar\":\n case \"name\":\n return { ...outputType, dbType: \"text\" };\n case \"int2\":\n case \"int4\":\n case \"int8\":\n case \"numeric\":\n case \"float4\":\n case \"float8\":\n case \"text\":\n case \"bpchar\":\n case \"date\":\n case \"time\":\n case \"timetz\":\n case \"timestamp\":\n case \"timestamptz\":\n case \"interval\":\n case \"money\":\n case \"inet\":\n return outputType;\n default:\n return undefined;\n }\n}\n\n/** An `AliasResolver` backed by an `ExprContext`; a handle's bound meta is its own (`em.query` sources are their own tables). */\nfunction ctxResolver(ctx: ExprContext): AliasResolver {\n return function resolve(handle) {\n return { meta: handle.meta, alias: ctx.aliasFor(handle) };\n };\n}\n\n/** Decodes `count`/`sum`/`avg` results, which Postgres returns as strings for bigint/numeric. */\nfunction decodeNumber(value: unknown): unknown {\n return typeof value === \"string\" ? Number(value) : value;\n}\n\nfunction identity(value: unknown): unknown {\n return value;\n}\n\nfunction fail(message: string): never {\n throw new Error(message);\n}\n"],"mappings":"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"}
|
|
1
|
+
{"version":3,"file":"Expr.cjs","names":["skipCondition","safeKq"],"sources":["../src/Expr.ts"],"sourcesContent":["import type { ExpressionCondition } from \"./EntityFilter.ts\";\nimport type { EntityMetadata } from \"./EntityMetadata.ts\";\nimport { safeKq } from \"./keywords.ts\";\nimport type { RawCondition } from \"./QueryParser.ts\";\nimport { skipCondition } from \"./skipCondition.ts\";\n\n/**\n * The shared expression protocol for `em.query`.\n *\n * Table columns (`a.first_name`), aggregates (`b.id.count()`), `sql` templates, subquery columns\n * (`bookStats.bookCount`), and scalar subqueries all implement it, so any of them can appear in\n * `select`, `where`, `groupBy`, `having`, `orderBy`, and inside other expressions.\n *\n * This module is a leaf on purpose: `Tables.ts` extends `BaseExpr` at load time, so nothing here may\n * import (at runtime) a module that leads back to `Tables.ts`. Anything that needs metadata, alias\n * binding, or SQL generation for conditions is reached through the `ExprContext` the query parser passes in.\n */\n\nexport const exprBrand: unique symbol = Symbol(\"joist.expr\");\n\n/**\n * Phantom type information carried by every `Expr`.\n *\n * `R` is the decoded result type.\n *\n * `Src` is the expression's *source key*: the type-level identity of the table it reads from. An entity\n * table's key is its type name (`table(Author)` gives `\"Author\"`) or the explicit name in\n * `table(Author, \"m\")`; a subquery's key is its `as: \"book_stats\"`, or the shared sentinel `\"?\"` when it\n * has no `as`. Exactly two questions are asked of a source key, and nothing else:\n *\n * - `MaybeNull` asks \"is my source key among the LEFT-joined sources in this query's join list?\" If yes,\n * the value can be `null`.\n * - `CheckScope` asks \"is my source key among `from` + `join` at all?\" If no, the query reads from a\n * table it never joined.\n *\n * Two special keys opt out of both questions:\n *\n * - `Expr<number, never>` reads from nothing that can be left-joined away, i.e. `b.id.count()`\n * - `Expr<number, string>` (the default) is untracked, i.e. `sql.ref` on an unknown table\n */\nexport interface ExprBrand<R, Src extends string> {\n readonly __result: R;\n readonly __source: Src;\n}\n\n/**\n * Runtime type information that lets set operations reject outputs that cannot safely share conversions.\n *\n * PostgreSQL compares projected SQL rows and chooses a common SQL type before Joist decodes them.\n * A compound uses one decoder per output column and retains an encoder for later comparisons and\n * coalesce fallbacks. We must check every operand before reusing the first operand's conversions:\n * TypeScript result types are erased, and equal SQL storage types do not imply equal logical domains.\n *\n * I.e. Author.id and Book.author_id are compatible Author IDs despite having separate serdes, while\n * Book.id must not compare or decode as an Author ID just because both use int4. Conversely, Author.age\n * and Author.age.sum() both expose TypeScript numbers but produce int4 and int8 driver values. Without\n * their SQL type information, accepting them could make decoding depend on which operand comes first.\n * Enum, custom-mapper, and JSON-schema domain tokens also prevent unrelated conversions from mixing.\n *\n * Compatibility requires exact equality of the canonical SQL `dbType`, the `domain` token, and\n * `idMeta`. An absent descriptor is unknown, not an identity codec. `idMeta` identifies scalar IDs only;\n * arrays retain their element domain separately so they cannot select a polymorphic IN component.\n * Raw `sql<R>` annotations do not supply this runtime information. Encoding and decoding remain on the\n * expression; this descriptor only holds the information needed for these conservative checks.\n */\nexport interface ExprOutputType {\n dbType: string;\n domain: unknown;\n idMeta?: EntityMetadata;\n /** Internal conversion proof, consistent for equal dbType/domain/idMeta; not another compatibility key. */\n arrayElementSafe?: boolean;\n}\n\n/**\n * \"Any expression whose result is `R`\", checked by brand alone.\n *\n * Method parameters use this instead of `Expr<R>` so that `Expr` stays covariant in `R`: checking\n * `Expr<AuthorId>` against `Expr<AuthorId | null>` then only compares the phantom `__result`, not every\n * method's parameter list (which would make `Expr` invariant, and break `select: b.author_id` dispatch and\n * polymorphic joins).\n */\nexport type ExprLike<R> = { readonly [exprBrand]: ExprBrand<R, any> };\n\n/**\n * A typed SQL expression: a table column, an aggregate, a `sql` template, or a scalar subquery.\n *\n * Conditions and SQL functions are methods, so they need no import. Aggregates keep `Src` so scope\n * checking still sees through `bs.x.max()`; `count` is source-less because `count(x)` is 0, not null,\n * when `x`'s table is left-joined away; `coalesce` drops `Src` on purpose, since its whole job is to\n * remove the nullability a left join adds.\n */\nexport interface Expr<R, Src extends string = string> {\n readonly [exprBrand]: ExprBrand<R, Src>;\n eq(value: R | ExprLike<R> | undefined): ExpressionCondition;\n ne(value: R | ExprLike<R> | undefined): ExpressionCondition;\n gt(value: R | ExprLike<R> | undefined): ExpressionCondition;\n gte(value: R | ExprLike<R> | undefined): ExpressionCondition;\n lt(value: R | ExprLike<R> | undefined): ExpressionCondition;\n lte(value: R | ExprLike<R> | undefined): ExpressionCondition;\n // A list subquery may select a nullable column; NULLs in the set never match, so that is fine.\n in(values: readonly R[] | ExprLike<R | null> | undefined): ExpressionCondition;\n nin(values: readonly R[] | ExprLike<R | null> | undefined): ExpressionCondition;\n\n /** `count(x)::int`; `count(a.id)` is `count(*)` for the FROM table, and the matched-row count for a left-joined one. */\n count(): Expr<number, never>;\n countDistinct(): Expr<number, never>;\n sum(this: Expr<number | null, Src>): Expr<number | null, Src>;\n avg(this: Expr<number | null, Src>): Expr<number | null, Src>;\n min(): Expr<R | null, Src>;\n max(): Expr<R | null, Src>;\n /** PG keeps element NULLs (a left-joined empty group aggregates as `[null]`), and zero rows aggregate as NULL. */\n arrayAgg(): Expr<R[] | null, Src>;\n stringAgg(this: Expr<string | null, Src>, delimiter: string): Expr<string | null, Src>;\n coalesce(fallback: NonNullable<R>): Expr<NonNullable<R>, never>;\n}\n\n/**\n * A join entry: the join kind is the key, the joined source is the value, plus `on`. `inner?: never` /\n * `left?: never` keep an entry to one kind (the `ExpressionFilter` `and`/`or` trick).\n *\n * `on` is required. A join is pruned when nothing references it anymore, not by an `undefined` ON;\n * `keep: true` pins a join that would otherwise prune, i.e. an inner join used as an existence filter,\n * the way em.find's `keepAliases` does. It is a boolean so callers can pass a flag.\n *\n * Declared here (not `query.ts`) so the relation join factories in `Tables.ts` (i.e. `a.books.as(b)`) can\n * return them without importing `query.ts`; `query.ts` re-constrains `A` to its `QuerySource`.\n */\nexport interface InnerJoin<A> {\n readonly inner: A;\n readonly left?: never;\n readonly on: ExpressionCondition;\n readonly keep?: boolean;\n}\n\nexport interface LeftJoin<A> {\n readonly left: A;\n readonly inner?: never;\n readonly on: ExpressionCondition;\n readonly keep?: boolean;\n}\n\n/** SQL plus its `?` bindings plus the SQL aliases it references, i.e. for join pruning. */\nexport interface SqlFragment {\n sql: string;\n bindings: any[];\n refs: string[];\n}\n\n/**\n * What an expression needs from the query it is generating SQL for: the SQL alias assigned to each\n * source, and a way to turn nested conditions into SQL (which needs `ConditionBuilder`, so it lives in `query.ts`).\n */\nexport interface ExprContext {\n /** Returns the SQL alias for a table's `TableMgmt` or a subquery handle, searching enclosing queries. */\n aliasFor(handle: object): string;\n /** Turns a user-facing condition into SQL; `undefined` if it pruned away entirely. */\n conditionToSql(cond: ExpressionCondition): SqlFragment | undefined;\n}\n\nexport function isExpr(value: unknown): value is ExprLike<any> {\n return typeof value === \"object\" && value !== null && exprBrand in value;\n}\n\n/** Every `Expr` is a `BaseExpr` at runtime; this cast keeps `isExpr` a plain type guard so unions narrow. */\nexport function asNode(expr: ExprLike<any>): BaseExpr {\n return expr as any as BaseExpr;\n}\n\nexport const deferredSym: unique symbol = Symbol(\"joist.deferredCondition\");\n\n/**\n * A condition whose SQL depends on aliases that are only known once the query is parsed, i.e.\n * `bookStats.bookCount.gt(1)` or `bs.authorId.eq(a.id)`.\n *\n * It is shaped like a `RawCondition` so it can sit in any `ExpressionFilter`; `resolveDeferredConditions`\n * snapshots `condition`, `bindings`, and `aliases` before the filter is parsed. Domain alias conditions\n * use a separate protocol in the `em.find` parser.\n */\nexport interface DeferredCondition extends RawCondition {\n [deferredSym]: (ctx: ExprContext) => RawCondition;\n}\n\nexport function isDeferredCondition(cond: unknown): cond is DeferredCondition {\n return typeof cond === \"object\" && cond !== null && deferredSym in cond;\n}\n\n/** Creates a `DeferredCondition` that generates its SQL with `fn` once the query's aliases are known. */\nexport function deferredCondition(fn: (ctx: ExprContext) => SqlFragment): DeferredCondition {\n const cond: DeferredCondition = {\n kind: \"raw\",\n aliases: [],\n condition: \"<unresolved>\",\n bindings: [],\n pruneable: false,\n [deferredSym]: (ctx) => {\n const { sql, bindings, refs } = fn(ctx);\n return { ...cond, condition: sql, bindings, aliases: refs };\n },\n };\n return cond;\n}\n\n/**\n * Resolve each condition occurrence and snapshot it before resolving the next one. A nested subquery\n * may reuse the same condition under another alias; it must not overwrite this occurrence's SQL.\n */\nexport function resolveDeferredConditions(\n cond: ExpressionCondition | undefined,\n ctx: ExprContext,\n): ExpressionCondition | undefined {\n if (cond === undefined || cond === null) return cond;\n if (isDeferredCondition(cond)) {\n return cond[deferredSym](ctx);\n } else if (\"and\" in cond && cond.and) {\n return { ...cond, and: cond.and.map((c) => resolveDeferredConditions(c, ctx)) };\n } else if (\"or\" in cond && cond.or) {\n return { ...cond, or: cond.or.map((c) => resolveDeferredConditions(c, ctx)) };\n }\n return cond;\n}\n\n/** Concatenates SQL fragments with `sep`, keeping bindings and refs in order. */\nexport function joinFragments(parts: SqlFragment[], sep: string): SqlFragment {\n return {\n sql: parts.map((p) => p.sql).join(sep),\n bindings: parts.flatMap((p) => p.bindings),\n refs: parts.flatMap((p) => p.refs),\n };\n}\n\n/**\n * The methods every expression shares. Subclasses provide `toSql`, and usually `decode`/`encode`.\n *\n * Table columns can override comparisons to apply column-specific conversions. Domain aliases do not\n * implement this protocol.\n */\nexport abstract class BaseExpr {\n readonly [exprBrand]: any = this;\n\n /** Produces this expression's SQL so it can be embedded in a larger expression, i.e. a subquery gets parens. */\n abstract toSql(ctx: ExprContext): SqlFragment;\n\n /** Only an actual scalar/IN subquery exposes its selected expression, not an ordinary ID expression. */\n get subquerySelect(): BaseExpr | undefined {\n return undefined;\n }\n\n /** Known SQL representation and logical domain; raw SQL and unmodeled refs remain unknown. */\n get outputType(): ExprOutputType | undefined {\n return undefined;\n }\n\n /** Physical SQL nullability; undefined means unknown, not a NOT NULL guarantee. */\n get sqlNullable(): boolean | undefined {\n return undefined;\n }\n\n /** The source of a direct column reference, whose value becomes NULL under an unmatched LEFT join. */\n get sqlSource(): object | undefined {\n return undefined;\n }\n\n /** Produces the SQL without the outer parens a subquery normally gets; only differs for subqueries. */\n toSqlBare(ctx: ExprContext): SqlFragment {\n return this.toSql(ctx);\n }\n\n /** Converts a result-set value into the domain value, i.e. an int into a tagged id. */\n decode(value: unknown): unknown {\n return value;\n }\n\n /** Converts a domain value into the database value, i.e. a tagged id into an int, for bindings. */\n encode(value: unknown): unknown {\n return value;\n }\n\n eq(value: unknown): ExpressionCondition {\n return this.compare(\"=\", value);\n }\n\n ne(value: unknown): ExpressionCondition {\n return this.compare(\"!=\", value);\n }\n\n gt(value: unknown): ExpressionCondition {\n return this.compare(\">\", value);\n }\n\n gte(value: unknown): ExpressionCondition {\n return this.compare(\">=\", value);\n }\n\n lt(value: unknown): ExpressionCondition {\n return this.compare(\"<\", value);\n }\n\n lte(value: unknown): ExpressionCondition {\n return this.compare(\"<=\", value);\n }\n\n in(values: unknown): ExpressionCondition {\n return this.inList(\"IN\", values);\n }\n\n nin(values: unknown): ExpressionCondition {\n return this.inList(\"NOT IN\", values);\n }\n\n count(): Expr<number, never> {\n return new FnExpr(\"count\", [this], {\n suffix: \"::int\",\n decode: decodeNumber,\n encode: identity,\n outputType: { dbType: \"int4\", domain: Number, arrayElementSafe: true },\n }) as any;\n }\n\n countDistinct(): Expr<number, never> {\n return new FnExpr(\"count\", [this], {\n prefix: \"DISTINCT \",\n suffix: \"::int\",\n decode: decodeNumber,\n encode: identity,\n outputType: { dbType: \"int4\", domain: Number, arrayElementSafe: true },\n }) as any;\n }\n\n sum(): Expr<number | null, any> {\n return new FnExpr(\"sum\", [this], {\n decode: decodeNumber,\n encode: identity,\n outputType: numericAggregateOutputType(\"sum\", this.outputType),\n }) as any;\n }\n\n avg(): Expr<number | null, any> {\n return new FnExpr(\"avg\", [this], {\n decode: decodeNumber,\n encode: identity,\n outputType: numericAggregateOutputType(\"avg\", this.outputType),\n }) as any;\n }\n\n min(): Expr<any, any> {\n return new FnExpr(\"min\", [this], {\n decode: (v) => this.decode(v),\n outputType: minMaxOutputType(this.outputType),\n }) as any;\n }\n\n max(): Expr<any, any> {\n return new FnExpr(\"max\", [this], {\n decode: (v) => this.decode(v),\n outputType: minMaxOutputType(this.outputType),\n }) as any;\n }\n\n arrayAgg(): Expr<any, any> {\n // Values are arrays while the argument encodes/decodes *elements*, i.e. a `.coalesce([\"b:1\"])`\n // fallback must encode each tagged id, not hand the whole array to the id column's encoder\n return new FnExpr(\"array_agg\", [this], {\n decode: (v) => (Array.isArray(v) ? v.map((e) => this.decode(e)) : v),\n encode: (v) => (Array.isArray(v) ? v.map((e) => this.encode(e)) : v),\n outputType: arrayOutputType(this.outputType),\n }) as any;\n }\n\n stringAgg(delimiter: string): Expr<string | null, any> {\n return new FnExpr(\"string_agg\", [this, new BindingExpr(delimiter)], {\n outputType:\n this.outputType?.domain === String ? { dbType: \"text\", domain: String, arrayElementSafe: true } : undefined,\n }) as any;\n }\n\n coalesce(fallback: unknown): Expr<any, never> {\n return new FnExpr(\"coalesce\", [this, new BindingExpr(this.encode(fallback))], {\n decode: (v) => this.decode(v),\n outputType: this.outputType,\n }) as any;\n }\n\n /** `this op value`, where `value` may be `undefined` (pruned), `null`, another expression, or a literal. */\n protected compare(op: string, value: unknown): ExpressionCondition {\n if (value === undefined) return skipCondition;\n if (value === null) {\n const not = op === \"=\" ? \"\" : op === \"!=\" ? \"NOT \" : fail(`Cannot compare ${op} to null`);\n return deferredCondition((ctx) => {\n const left = this.toSql(ctx);\n return { ...left, sql: `${left.sql} IS ${not}NULL` };\n });\n }\n if (isExpr(value)) {\n return deferredCondition((ctx) => joinFragments([this.toSql(ctx), asNode(value).toSql(ctx)], ` ${op} `));\n }\n return deferredCondition((ctx) => {\n const left = this.toSql(ctx);\n return { sql: `${left.sql} ${op} ?`, bindings: [...left.bindings, this.encode(value)], refs: left.refs };\n });\n }\n\n /** `this IN (subquery)` or `this = ANY(?)` for a list; `NOT IN` / `!= ALL(?)` for `nin`. */\n protected inList(op: \"IN\" | \"NOT IN\", values: unknown): ExpressionCondition {\n if (values === undefined) return skipCondition;\n if (isExpr(values)) {\n return deferredCondition((ctx) => {\n const left = this.toSql(ctx);\n const right = asNode(values).toSqlBare(ctx);\n return joinFragments([left, { ...right, sql: `(${right.sql})` }], ` ${op} `);\n });\n }\n if (!Array.isArray(values)) fail(`Expected an array or subquery for ${op}, got ${values}`);\n const fn = op === \"IN\" ? \"= ANY(?)\" : \"!= ALL(?)\";\n return deferredCondition((ctx) => {\n const left = this.toSql(ctx);\n return {\n sql: `${left.sql} ${fn}`,\n bindings: [...left.bindings, values.map((v) => this.encode(v))],\n refs: left.refs,\n };\n });\n }\n}\n\n/**\n * A SQL function applied to expressions, i.e. `count(a.\"id\")::int` or `coalesce(bs.\"n\", ?)`.\n *\n * By default decoding is identity and encoding follows the first argument. Callers explicitly supply\n * a decoder and output type when needed (`max(a.id)` is still an id); numeric aggregates supply their\n * own decoder/encoder, since `count(a.id)` is a number, not an id. Unknown functions have no output type.\n */\nexport class FnExpr extends BaseExpr {\n constructor(\n private name: string,\n private args: BaseExpr[],\n private opts: {\n prefix?: string;\n suffix?: string;\n decode?: (value: unknown) => unknown;\n encode?: (value: unknown) => unknown;\n outputType?: ExprOutputType;\n },\n ) {\n super();\n }\n\n get outputType(): ExprOutputType | undefined {\n return this.opts.outputType;\n }\n\n get sqlNullable(): boolean | undefined {\n switch (this.name) {\n case \"count\":\n return false;\n case \"sum\":\n case \"avg\":\n case \"min\":\n case \"max\":\n case \"array_agg\":\n case \"string_agg\":\n return true;\n case \"coalesce\":\n // Only the fallback is independent of an outer query's LEFT joins.\n return this.args[1]?.sqlNullable === false ? false : undefined;\n default:\n return undefined;\n }\n }\n\n toSql(ctx: ExprContext): SqlFragment {\n const args = joinFragments(\n this.args.map((a) => a.toSql(ctx)),\n \", \",\n );\n return { ...args, sql: `${this.name}(${this.opts.prefix ?? \"\"}${args.sql})${this.opts.suffix ?? \"\"}` };\n }\n\n decode(value: unknown): unknown {\n if (value === null || value === undefined) return value;\n return this.opts.decode ? this.opts.decode(value) : value;\n }\n\n encode(value: unknown): unknown {\n return this.opts.encode ? this.opts.encode(value) : this.args[0].encode(value);\n }\n}\n\n/** A bound literal, i.e. the `?` in `coalesce(x, ?)`. */\nexport class BindingExpr extends BaseExpr {\n constructor(private value: unknown) {\n super();\n }\n\n get sqlNullable(): boolean {\n return this.value === null || this.value === undefined;\n }\n\n toSql(): SqlFragment {\n return { sql: \"?\", bindings: [this.value], refs: [] };\n }\n}\n\n/** An unmodeled column on a known source, i.e. `sql.ref(a, \"ts_search\")`; untracked at the type level. */\nexport class RefExpr extends BaseExpr {\n constructor(\n private handle: object,\n private column: string,\n ) {\n super();\n }\n\n get sqlSource(): object {\n return this.handle;\n }\n\n toSql(ctx: ExprContext): SqlFragment {\n const alias = ctx.aliasFor(this.handle);\n // safeKq for both halves: sql.ref takes user strings, and a subquery alias is its `as` name\n return { sql: `${safeKq(alias)}.${safeKq(this.column)}`, bindings: [], refs: [alias] };\n }\n}\n\n/**\n * A `sql` tagged template.\n *\n * For an Author table `a` assigned the SQL alias `a1`:\n *\n * ```ts\n * sql`${a.age} * 2` // Expression: a1.age * 2\n * sql`${a.age.gte(18)}` // Condition: (a1.age >= ?), bindings [18]\n * sql`${\"Alice\"}` // Value: ?, bindings [\"Alice\"]\n * ```\n */\nexport class TemplateExpr extends BaseExpr {\n constructor(\n private strings: TemplateStringsArray,\n private values: unknown[],\n ) {\n super();\n }\n\n toSql(ctx: ExprContext): SqlFragment {\n const parts: SqlFragment[] = [];\n this.strings.forEach((s, i) => {\n parts.push({ sql: s, bindings: [], refs: [] });\n if (i < this.values.length) parts.push(interpolationToSql(this.values[i], ctx));\n });\n return joinFragments(parts, \"\");\n }\n}\n\n/** Turns one `${...}` of a `sql` template into SQL: an expression, a condition, or a bound value. */\nexport function interpolationToSql(value: unknown, ctx: ExprContext): SqlFragment {\n if (isExpr(value)) {\n return asNode(value).toSql(ctx);\n } else if (isConditionLike(value)) {\n return ctx.conditionToSql(value as ExpressionCondition) ?? { sql: \"true\", bindings: [], refs: [] };\n } else {\n return { sql: \"?\", bindings: [value], refs: [] };\n }\n}\n\n/** True for the user-facing condition shapes: `{ and }`, `{ or }`, `ColumnCondition`, `RawCondition`. */\nexport function isConditionLike(value: unknown): boolean {\n if (typeof value !== \"object\" || value === null) return false;\n const v = value as any;\n return \"and\" in v || \"or\" in v || v.kind === \"column\" || v.kind === \"raw\" || v.kind === \"exists\";\n}\n\n/**\n * Normalizes known PostgreSQL type synonyms without treating coercible types or user types as equal.\n * The output compatibility checks compare these names, so normalize only spellings of the same type.\n *\n * I.e. `integer` and `int` both mean `int4`; `integer[]` and `int4[]` also describe the same SQL type.\n * Keeping these spellings distinct would reject compatible outputs just because their serdes use\n * different names for the same representation.\n *\n * I.e. `int4` can be promoted to `int8`, but they are not synonyms: classic pg returns int4 as a number\n * and int8 as a string. Treating them as equal could admit a compound whose first operand's decoder\n * cannot handle the common SQL type. This function does not choose or apply a conversion.\n *\n * I.e. `citext` must stay distinct from `text`: they disagree on whether 'Alice' and 'alice' are equal,\n * which matters when a set operation compares rows. A user-defined `app.email` domain over text can\n * also impose constraints that text does not. Unknown names remain unchanged, including their schema\n * and quoting; neither coercibility nor a shared base type proves that their outputs are compatible.\n */\nexport function canonicalDbType(dbType: string): string {\n if (dbType.endsWith(\"[]\")) return `${canonicalDbType(dbType.slice(0, -2))}[]`;\n switch (dbType) {\n case \"smallint\":\n return \"int2\";\n case \"int\":\n case \"integer\":\n return \"int4\";\n case \"bigint\":\n return \"int8\";\n case \"decimal\":\n return \"numeric\";\n case \"real\":\n return \"float4\";\n case \"double precision\":\n return \"float8\";\n case \"boolean\":\n return \"bool\";\n case \"character varying\":\n return \"varchar\";\n case \"character\":\n return \"bpchar\";\n case \"timestamp without time zone\":\n return \"timestamp\";\n case \"timestamp with time zone\":\n return \"timestamptz\";\n case \"time without time zone\":\n return \"time\";\n case \"time with time zone\":\n return \"timetz\";\n default:\n return dbType;\n }\n}\n\nconst arrayDomains = new Map<unknown, Map<EntityMetadata | undefined, symbol>>();\n\n// Both classic pg and lazy binary reads decode these physical arrays, not an unparsed array string.\nconst arrayElementDbTypes = new Set([\n \"bool\",\n \"bytea\",\n \"int2\",\n \"int4\",\n \"int8\",\n \"float4\",\n \"float8\",\n \"numeric\",\n \"text\",\n \"varchar\",\n \"bpchar\",\n \"uuid\",\n \"json\",\n \"jsonb\",\n]);\n\n/**\n * Describes elementwise conversion only for known physical array parsers and stable domain conversion.\n *\n * I.e. `a.id.arrayAgg()` wraps Author's tag and `idMeta` in its domain token, but has no scalar\n * `idMeta` of its own. Repeated expressions share the token; IDs of subtypes with a shared tag do not.\n * Native enum/citext, date/time, and nested SQL arrays remain unknown. Numeric elements can be numbers\n * in classic pg and strings in lazy binary reads, so a scalar string-only mapper is not sufficient.\n */\nexport function arrayOutputType(outputType: ExprOutputType | undefined): ExprOutputType | undefined {\n if (!outputType?.arrayElementSafe || !arrayElementDbTypes.has(outputType.dbType)) return undefined;\n let domains = arrayDomains.get(outputType.domain);\n if (!domains) arrayDomains.set(outputType.domain, (domains = new Map()));\n let domain = domains.get(outputType.idMeta);\n if (!domain) domains.set(outputType.idMeta, (domain = Symbol(\"joist.arrayOutput\")));\n return { dbType: `${outputType.dbType}[]`, domain };\n}\n\n/**\n * Resolves the SQL result type of supported numeric aggregates; other overloads remain unknown.\n *\n * I.e. `a.age.sum()` has `dbType: \"int8\"` and Number conversion, while `a.age` has `dbType: \"int4\"`\n * and identity conversion. Their number domains agree, but SQL types reject a union in either order.\n */\nfunction numericAggregateOutputType(\n name: \"sum\" | \"avg\",\n outputType: ExprOutputType | undefined,\n): ExprOutputType | undefined {\n switch (outputType?.dbType) {\n case \"int2\":\n case \"int4\":\n return { dbType: name === \"sum\" ? \"int8\" : \"numeric\", domain: Number, arrayElementSafe: true };\n case \"int8\":\n case \"numeric\":\n return { dbType: \"numeric\", domain: Number, arrayElementSafe: true };\n case \"float4\":\n return { dbType: name === \"sum\" ? \"float4\" : \"float8\", domain: Number, arrayElementSafe: true };\n case \"float8\":\n return { dbType: \"float8\", domain: Number, arrayElementSafe: true };\n default:\n return undefined;\n }\n}\n\n/** Only known MIN/MAX overloads have predictable output types; varchar/name use the text overload. */\nfunction minMaxOutputType(outputType: ExprOutputType | undefined): ExprOutputType | undefined {\n switch (outputType?.dbType) {\n case \"varchar\":\n case \"name\":\n return { ...outputType, dbType: \"text\" };\n case \"int2\":\n case \"int4\":\n case \"int8\":\n case \"numeric\":\n case \"float4\":\n case \"float8\":\n case \"text\":\n case \"bpchar\":\n case \"date\":\n case \"time\":\n case \"timetz\":\n case \"timestamp\":\n case \"timestamptz\":\n case \"interval\":\n case \"money\":\n case \"inet\":\n return outputType;\n default:\n return undefined;\n }\n}\n\n/** Decodes `count`/`sum`/`avg` results, which Postgres returns as strings for bigint/numeric. */\nfunction decodeNumber(value: unknown): unknown {\n return typeof value === \"string\" ? Number(value) : value;\n}\n\nfunction identity(value: unknown): unknown {\n return value;\n}\n\nfunction fail(message: string): never {\n throw new Error(message);\n}\n"],"mappings":";;;;;;;;;;;;;;;AAkBA,MAAa,YAA2B,OAAO,YAAY;AA6I3D,SAAgB,OAAO,OAAwC;CAC7D,OAAO,OAAO,UAAU,YAAY,UAAU,QAAQ,aAAa;AACrE;;AAGA,SAAgB,OAAO,MAA+B;CACpD,OAAO;AACT;AAEA,MAAa,cAA6B,OAAO,yBAAyB;AAc1E,SAAgB,oBAAoB,MAA0C;CAC5E,OAAO,OAAO,SAAS,YAAY,SAAS,QAAQ,eAAe;AACrE;;AAGA,SAAgB,kBAAkB,IAA0D;CAC1F,MAAM,OAA0B;EAC9B,MAAM;EACN,SAAS,CAAC;EACV,WAAW;EACX,UAAU,CAAC;EACX,WAAW;GACV,eAAe,QAAQ;GACtB,MAAM,EAAE,KAAK,UAAU,SAAS,GAAG,GAAG;GACtC,OAAO;IAAE,GAAG;IAAM,WAAW;IAAK;IAAU,SAAS;GAAK;EAC5D;CACF;CACA,OAAO;AACT;;;;;AAMA,SAAgB,0BACd,MACA,KACiC;CACjC,IAAI,SAAS,KAAA,KAAa,SAAS,MAAM,OAAO;CAChD,IAAI,oBAAoB,IAAI,GAC1B,OAAO,KAAK,YAAY,CAAC,GAAG;MACvB,IAAI,SAAS,QAAQ,KAAK,KAC/B,OAAO;EAAE,GAAG;EAAM,KAAK,KAAK,IAAI,KAAK,MAAM,0BAA0B,GAAG,GAAG,CAAC;CAAE;MACzE,IAAI,QAAQ,QAAQ,KAAK,IAC9B,OAAO;EAAE,GAAG;EAAM,IAAI,KAAK,GAAG,KAAK,MAAM,0BAA0B,GAAG,GAAG,CAAC;CAAE;CAE9E,OAAO;AACT;;AAGA,SAAgB,cAAc,OAAsB,KAA0B;CAC5E,OAAO;EACL,KAAK,MAAM,KAAK,MAAM,EAAE,GAAG,CAAC,CAAC,KAAK,GAAG;EACrC,UAAU,MAAM,SAAS,MAAM,EAAE,QAAQ;EACzC,MAAM,MAAM,SAAS,MAAM,EAAE,IAAI;CACnC;AACF;;;;;;;AAQA,IAAsB,WAAtB,MAA+B;CAC7B,CAAU,aAAkB;;CAM5B,IAAI,iBAAuC,CAE3C;;CAGA,IAAI,aAAyC,CAE7C;;CAGA,IAAI,cAAmC,CAEvC;;CAGA,IAAI,YAAgC,CAEpC;;CAGA,UAAU,KAA+B;EACvC,OAAO,KAAK,MAAM,GAAG;CACvB;;CAGA,OAAO,OAAyB;EAC9B,OAAO;CACT;;CAGA,OAAO,OAAyB;EAC9B,OAAO;CACT;CAEA,GAAG,OAAqC;EACtC,OAAO,KAAK,QAAQ,KAAK,KAAK;CAChC;CAEA,GAAG,OAAqC;EACtC,OAAO,KAAK,QAAQ,MAAM,KAAK;CACjC;CAEA,GAAG,OAAqC;EACtC,OAAO,KAAK,QAAQ,KAAK,KAAK;CAChC;CAEA,IAAI,OAAqC;EACvC,OAAO,KAAK,QAAQ,MAAM,KAAK;CACjC;CAEA,GAAG,OAAqC;EACtC,OAAO,KAAK,QAAQ,KAAK,KAAK;CAChC;CAEA,IAAI,OAAqC;EACvC,OAAO,KAAK,QAAQ,MAAM,KAAK;CACjC;CAEA,GAAG,QAAsC;EACvC,OAAO,KAAK,OAAO,MAAM,MAAM;CACjC;CAEA,IAAI,QAAsC;EACxC,OAAO,KAAK,OAAO,UAAU,MAAM;CACrC;CAEA,QAA6B;EAC3B,OAAO,IAAI,OAAO,SAAS,CAAC,IAAI,GAAG;GACjC,QAAQ;GACR,QAAQ;GACR,QAAQ;GACR,YAAY;IAAE,QAAQ;IAAQ,QAAQ;IAAQ,kBAAkB;GAAK;EACvE,CAAC;CACH;CAEA,gBAAqC;EACnC,OAAO,IAAI,OAAO,SAAS,CAAC,IAAI,GAAG;GACjC,QAAQ;GACR,QAAQ;GACR,QAAQ;GACR,QAAQ;GACR,YAAY;IAAE,QAAQ;IAAQ,QAAQ;IAAQ,kBAAkB;GAAK;EACvE,CAAC;CACH;CAEA,MAAgC;EAC9B,OAAO,IAAI,OAAO,OAAO,CAAC,IAAI,GAAG;GAC/B,QAAQ;GACR,QAAQ;GACR,YAAY,2BAA2B,OAAO,KAAK,UAAU;EAC/D,CAAC;CACH;CAEA,MAAgC;EAC9B,OAAO,IAAI,OAAO,OAAO,CAAC,IAAI,GAAG;GAC/B,QAAQ;GACR,QAAQ;GACR,YAAY,2BAA2B,OAAO,KAAK,UAAU;EAC/D,CAAC;CACH;CAEA,MAAsB;EACpB,OAAO,IAAI,OAAO,OAAO,CAAC,IAAI,GAAG;GAC/B,SAAS,MAAM,KAAK,OAAO,CAAC;GAC5B,YAAY,iBAAiB,KAAK,UAAU;EAC9C,CAAC;CACH;CAEA,MAAsB;EACpB,OAAO,IAAI,OAAO,OAAO,CAAC,IAAI,GAAG;GAC/B,SAAS,MAAM,KAAK,OAAO,CAAC;GAC5B,YAAY,iBAAiB,KAAK,UAAU;EAC9C,CAAC;CACH;CAEA,WAA2B;EAGzB,OAAO,IAAI,OAAO,aAAa,CAAC,IAAI,GAAG;GACrC,SAAS,MAAO,MAAM,QAAQ,CAAC,IAAI,EAAE,KAAK,MAAM,KAAK,OAAO,CAAC,CAAC,IAAI;GAClE,SAAS,MAAO,MAAM,QAAQ,CAAC,IAAI,EAAE,KAAK,MAAM,KAAK,OAAO,CAAC,CAAC,IAAI;GAClE,YAAY,gBAAgB,KAAK,UAAU;EAC7C,CAAC;CACH;CAEA,UAAU,WAA6C;EACrD,OAAO,IAAI,OAAO,cAAc,CAAC,MAAM,IAAI,YAAY,SAAS,CAAC,GAAG,EAClE,YACE,KAAK,YAAY,WAAW,SAAS;GAAE,QAAQ;GAAQ,QAAQ;GAAQ,kBAAkB;EAAK,IAAI,KAAA,EACtG,CAAC;CACH;CAEA,SAAS,UAAqC;EAC5C,OAAO,IAAI,OAAO,YAAY,CAAC,MAAM,IAAI,YAAY,KAAK,OAAO,QAAQ,CAAC,CAAC,GAAG;GAC5E,SAAS,MAAM,KAAK,OAAO,CAAC;GAC5B,YAAY,KAAK;EACnB,CAAC;CACH;;CAGA,QAAkB,IAAY,OAAqC;EACjE,IAAI,UAAU,KAAA,GAAW,OAAOA,sBAAAA;EAChC,IAAI,UAAU,MAAM;GAClB,MAAM,MAAM,OAAO,MAAM,KAAK,OAAO,OAAO,SAAS,KAAK,kBAAkB,GAAG,SAAS;GACxF,OAAO,mBAAmB,QAAQ;IAChC,MAAM,OAAO,KAAK,MAAM,GAAG;IAC3B,OAAO;KAAE,GAAG;KAAM,KAAK,GAAG,KAAK,IAAI,MAAM,IAAI;IAAM;GACrD,CAAC;EACH;EACA,IAAI,OAAO,KAAK,GACd,OAAO,mBAAmB,QAAQ,cAAc,CAAC,KAAK,MAAM,GAAG,GAAG,OAAO,KAAK,CAAC,CAAC,MAAM,GAAG,CAAC,GAAG,IAAI,GAAG,EAAE,CAAC;EAEzG,OAAO,mBAAmB,QAAQ;GAChC,MAAM,OAAO,KAAK,MAAM,GAAG;GAC3B,OAAO;IAAE,KAAK,GAAG,KAAK,IAAI,GAAG,GAAG;IAAK,UAAU,CAAC,GAAG,KAAK,UAAU,KAAK,OAAO,KAAK,CAAC;IAAG,MAAM,KAAK;GAAK;EACzG,CAAC;CACH;;CAGA,OAAiB,IAAqB,QAAsC;EAC1E,IAAI,WAAW,KAAA,GAAW,OAAOA,sBAAAA;EACjC,IAAI,OAAO,MAAM,GACf,OAAO,mBAAmB,QAAQ;GAChC,MAAM,OAAO,KAAK,MAAM,GAAG;GAC3B,MAAM,QAAQ,OAAO,MAAM,CAAC,CAAC,UAAU,GAAG;GAC1C,OAAO,cAAc,CAAC,MAAM;IAAE,GAAG;IAAO,KAAK,IAAI,MAAM,IAAI;GAAG,CAAC,GAAG,IAAI,GAAG,EAAE;EAC7E,CAAC;EAEH,IAAI,CAAC,MAAM,QAAQ,MAAM,GAAG,KAAK,qCAAqC,GAAG,QAAQ,QAAQ;EACzF,MAAM,KAAK,OAAO,OAAO,aAAa;EACtC,OAAO,mBAAmB,QAAQ;GAChC,MAAM,OAAO,KAAK,MAAM,GAAG;GAC3B,OAAO;IACL,KAAK,GAAG,KAAK,IAAI,GAAG;IACpB,UAAU,CAAC,GAAG,KAAK,UAAU,OAAO,KAAK,MAAM,KAAK,OAAO,CAAC,CAAC,CAAC;IAC9D,MAAM,KAAK;GACb;EACF,CAAC;CACH;AACF;;;;;;;;AASA,IAAa,SAAb,cAA4B,SAAS;CAEzB;CACA;CACA;CAHV,YACE,MACA,MACA,MAOA;EACA,MAAM;EAVE,KAAA,OAAA;EACA,KAAA,OAAA;EACA,KAAA,OAAA;CASV;CAEA,IAAI,aAAyC;EAC3C,OAAO,KAAK,KAAK;CACnB;CAEA,IAAI,cAAmC;EACrC,QAAQ,KAAK,MAAb;GACE,KAAK,SACH,OAAO;GACT,KAAK;GACL,KAAK;GACL,KAAK;GACL,KAAK;GACL,KAAK;GACL,KAAK,cACH,OAAO;GACT,KAAK,YAEH,OAAO,KAAK,KAAK,EAAE,EAAE,gBAAgB,QAAQ,QAAQ,KAAA;GACvD,SACE;EACJ;CACF;CAEA,MAAM,KAA+B;EACnC,MAAM,OAAO,cACX,KAAK,KAAK,KAAK,MAAM,EAAE,MAAM,GAAG,CAAC,GACjC,IACF;EACA,OAAO;GAAE,GAAG;GAAM,KAAK,GAAG,KAAK,KAAK,GAAG,KAAK,KAAK,UAAU,KAAK,KAAK,IAAI,GAAG,KAAK,KAAK,UAAU;EAAK;CACvG;CAEA,OAAO,OAAyB;EAC9B,IAAI,UAAU,QAAQ,UAAU,KAAA,GAAW,OAAO;EAClD,OAAO,KAAK,KAAK,SAAS,KAAK,KAAK,OAAO,KAAK,IAAI;CACtD;CAEA,OAAO,OAAyB;EAC9B,OAAO,KAAK,KAAK,SAAS,KAAK,KAAK,OAAO,KAAK,IAAI,KAAK,KAAK,EAAE,CAAC,OAAO,KAAK;CAC/E;AACF;;AAGA,IAAa,cAAb,cAAiC,SAAS;CACpB;CAApB,YAAY,OAAwB;EAClC,MAAM;EADY,KAAA,QAAA;CAEpB;CAEA,IAAI,cAAuB;EACzB,OAAO,KAAK,UAAU,QAAQ,KAAK,UAAU,KAAA;CAC/C;CAEA,QAAqB;EACnB,OAAO;GAAE,KAAK;GAAK,UAAU,CAAC,KAAK,KAAK;GAAG,MAAM,CAAC;EAAE;CACtD;AACF;;AAGA,IAAa,UAAb,cAA6B,SAAS;CAE1B;CACA;CAFV,YACE,QACA,QACA;EACA,MAAM;EAHE,KAAA,SAAA;EACA,KAAA,SAAA;CAGV;CAEA,IAAI,YAAoB;EACtB,OAAO,KAAK;CACd;CAEA,MAAM,KAA+B;EACnC,MAAM,QAAQ,IAAI,SAAS,KAAK,MAAM;EAEtC,OAAO;GAAE,KAAK,GAAGC,iBAAAA,OAAO,KAAK,EAAE,GAAGA,iBAAAA,OAAO,KAAK,MAAM;GAAK,UAAU,CAAC;GAAG,MAAM,CAAC,KAAK;EAAE;CACvF;AACF;;;;;;;;;;;;AAaA,IAAa,eAAb,cAAkC,SAAS;CAE/B;CACA;CAFV,YACE,SACA,QACA;EACA,MAAM;EAHE,KAAA,UAAA;EACA,KAAA,SAAA;CAGV;CAEA,MAAM,KAA+B;EACnC,MAAM,QAAuB,CAAC;EAC9B,KAAK,QAAQ,SAAS,GAAG,MAAM;GAC7B,MAAM,KAAK;IAAE,KAAK;IAAG,UAAU,CAAC;IAAG,MAAM,CAAC;GAAE,CAAC;GAC7C,IAAI,IAAI,KAAK,OAAO,QAAQ,MAAM,KAAK,mBAAmB,KAAK,OAAO,IAAI,GAAG,CAAC;EAChF,CAAC;EACD,OAAO,cAAc,OAAO,EAAE;CAChC;AACF;;AAGA,SAAgB,mBAAmB,OAAgB,KAA+B;CAChF,IAAI,OAAO,KAAK,GACd,OAAO,OAAO,KAAK,CAAC,CAAC,MAAM,GAAG;MACzB,IAAI,gBAAgB,KAAK,GAC9B,OAAO,IAAI,eAAe,KAA4B,KAAK;EAAE,KAAK;EAAQ,UAAU,CAAC;EAAG,MAAM,CAAC;CAAE;MAEjG,OAAO;EAAE,KAAK;EAAK,UAAU,CAAC,KAAK;EAAG,MAAM,CAAC;CAAE;AAEnD;;AAGA,SAAgB,gBAAgB,OAAyB;CACvD,IAAI,OAAO,UAAU,YAAY,UAAU,MAAM,OAAO;CACxD,MAAM,IAAI;CACV,OAAO,SAAS,KAAK,QAAQ,KAAK,EAAE,SAAS,YAAY,EAAE,SAAS,SAAS,EAAE,SAAS;AAC1F;;;;;;;;;;;;;;;;;;AAmBA,SAAgB,gBAAgB,QAAwB;CACtD,IAAI,OAAO,SAAS,IAAI,GAAG,OAAO,GAAG,gBAAgB,OAAO,MAAM,GAAG,EAAE,CAAC,EAAE;CAC1E,QAAQ,QAAR;EACE,KAAK,YACH,OAAO;EACT,KAAK;EACL,KAAK,WACH,OAAO;EACT,KAAK,UACH,OAAO;EACT,KAAK,WACH,OAAO;EACT,KAAK,QACH,OAAO;EACT,KAAK,oBACH,OAAO;EACT,KAAK,WACH,OAAO;EACT,KAAK,qBACH,OAAO;EACT,KAAK,aACH,OAAO;EACT,KAAK,+BACH,OAAO;EACT,KAAK,4BACH,OAAO;EACT,KAAK,0BACH,OAAO;EACT,KAAK,uBACH,OAAO;EACT,SACE,OAAO;CACX;AACF;AAEA,MAAM,+BAAe,IAAI,IAAsD;AAG/E,MAAM,sCAAsB,IAAI,IAAI;CAClC;CACA;CACA;CACA;CACA;CACA;CACA;CACA;CACA;CACA;CACA;CACA;CACA;CACA;AACF,CAAC;;;;;;;;;AAUD,SAAgB,gBAAgB,YAAoE;CAClG,IAAI,CAAC,YAAY,oBAAoB,CAAC,oBAAoB,IAAI,WAAW,MAAM,GAAG,OAAO,KAAA;CACzF,IAAI,UAAU,aAAa,IAAI,WAAW,MAAM;CAChD,IAAI,CAAC,SAAS,aAAa,IAAI,WAAW,QAAS,0BAAU,IAAI,IAAI,CAAE;CACvE,IAAI,SAAS,QAAQ,IAAI,WAAW,MAAM;CAC1C,IAAI,CAAC,QAAQ,QAAQ,IAAI,WAAW,QAAS,SAAS,OAAO,mBAAmB,CAAE;CAClF,OAAO;EAAE,QAAQ,GAAG,WAAW,OAAO;EAAK;CAAO;AACpD;;;;;;;AAQA,SAAS,2BACP,MACA,YAC4B;CAC5B,QAAQ,YAAY,QAApB;EACE,KAAK;EACL,KAAK,QACH,OAAO;GAAE,QAAQ,SAAS,QAAQ,SAAS;GAAW,QAAQ;GAAQ,kBAAkB;EAAK;EAC/F,KAAK;EACL,KAAK,WACH,OAAO;GAAE,QAAQ;GAAW,QAAQ;GAAQ,kBAAkB;EAAK;EACrE,KAAK,UACH,OAAO;GAAE,QAAQ,SAAS,QAAQ,WAAW;GAAU,QAAQ;GAAQ,kBAAkB;EAAK;EAChG,KAAK,UACH,OAAO;GAAE,QAAQ;GAAU,QAAQ;GAAQ,kBAAkB;EAAK;EACpE,SACE;CACJ;AACF;;AAGA,SAAS,iBAAiB,YAAoE;CAC5F,QAAQ,YAAY,QAApB;EACE,KAAK;EACL,KAAK,QACH,OAAO;GAAE,GAAG;GAAY,QAAQ;EAAO;EACzC,KAAK;EACL,KAAK;EACL,KAAK;EACL,KAAK;EACL,KAAK;EACL,KAAK;EACL,KAAK;EACL,KAAK;EACL,KAAK;EACL,KAAK;EACL,KAAK;EACL,KAAK;EACL,KAAK;EACL,KAAK;EACL,KAAK;EACL,KAAK,QACH,OAAO;EACT,SACE;CACJ;AACF;;AAGA,SAAS,aAAa,OAAyB;CAC7C,OAAO,OAAO,UAAU,WAAW,OAAO,KAAK,IAAI;AACrD;AAEA,SAAS,SAAS,OAAyB;CACzC,OAAO;AACT;AAEA,SAAS,KAAK,SAAwB;CACpC,MAAM,IAAI,MAAM,OAAO;AACzB"}
|
package/build/Expr.d.cts
CHANGED
|
@@ -1,4 +1,3 @@
|
|
|
1
|
-
import { AliasResolver, DeferredAliasCondition, deferredAliasSym, isDeferredAliasCondition, skipCondition, withDeferredAlias } from "./DeferredAlias.cjs";
|
|
2
1
|
import { EntityMetadata } from "./EntityMetadata.cjs";
|
|
3
2
|
import { ExpressionCondition } from "./EntityFilter.cjs";
|
|
4
3
|
import { RawCondition } from "./QueryParser.cjs";
|
|
@@ -141,7 +140,7 @@ interface SqlFragment {
|
|
|
141
140
|
* source, and a way to turn nested conditions into SQL (which needs `ConditionBuilder`, so it lives in `query.ts`).
|
|
142
141
|
*/
|
|
143
142
|
interface ExprContext {
|
|
144
|
-
/** Returns the SQL alias for
|
|
143
|
+
/** Returns the SQL alias for a table's `TableMgmt` or a subquery handle, searching enclosing queries. */
|
|
145
144
|
aliasFor(handle: object): string;
|
|
146
145
|
/** Turns a user-facing condition into SQL; `undefined` if it pruned away entirely. */
|
|
147
146
|
conditionToSql(cond: ExpressionCondition): SqlFragment | undefined;
|
|
@@ -155,9 +154,8 @@ declare const deferredSym: unique symbol;
|
|
|
155
154
|
* `bookStats.bookCount.gt(1)` or `bs.authorId.eq(a.id)`.
|
|
156
155
|
*
|
|
157
156
|
* It is shaped like a `RawCondition` so it can sit in any `ExpressionFilter`; `resolveDeferredConditions`
|
|
158
|
-
* snapshots `condition`, `bindings`, and `aliases` before the filter is parsed.
|
|
159
|
-
*
|
|
160
|
-
* involve a non-alias expression.
|
|
157
|
+
* snapshots `condition`, `bindings`, and `aliases` before the filter is parsed. Domain alias conditions
|
|
158
|
+
* use a separate protocol in the `em.find` parser.
|
|
161
159
|
*/
|
|
162
160
|
interface DeferredCondition extends RawCondition {
|
|
163
161
|
[deferredSym]: (ctx: ExprContext) => RawCondition;
|
|
@@ -175,8 +173,8 @@ declare function joinFragments(parts: SqlFragment[], sep: string): SqlFragment;
|
|
|
175
173
|
/**
|
|
176
174
|
* The methods every expression shares. Subclasses provide `toSql`, and usually `decode`/`encode`.
|
|
177
175
|
*
|
|
178
|
-
*
|
|
179
|
-
*
|
|
176
|
+
* Table columns can override comparisons to apply column-specific conversions. Domain aliases do not
|
|
177
|
+
* implement this protocol.
|
|
180
178
|
*/
|
|
181
179
|
declare abstract class BaseExpr {
|
|
182
180
|
readonly [exprBrand]: any;
|
|
@@ -260,7 +258,7 @@ declare class RefExpr extends BaseExpr {
|
|
|
260
258
|
/**
|
|
261
259
|
* A `sql` tagged template.
|
|
262
260
|
*
|
|
263
|
-
* For an Author
|
|
261
|
+
* For an Author table `a` assigned the SQL alias `a1`:
|
|
264
262
|
*
|
|
265
263
|
* ```ts
|
|
266
264
|
* sql`${a.age} * 2` // Expression: a1.age * 2
|
|
@@ -306,5 +304,5 @@ declare function canonicalDbType(dbType: string): string;
|
|
|
306
304
|
*/
|
|
307
305
|
declare function arrayOutputType(outputType: ExprOutputType | undefined): ExprOutputType | undefined;
|
|
308
306
|
//#endregion
|
|
309
|
-
export {
|
|
307
|
+
export { BaseExpr, BindingExpr, DeferredCondition, Expr, ExprBrand, ExprContext, ExprLike, ExprOutputType, FnExpr, InnerJoin, LeftJoin, RefExpr, SqlFragment, TemplateExpr, arrayOutputType, asNode, canonicalDbType, deferredCondition, deferredSym, exprBrand, interpolationToSql, isConditionLike, isDeferredCondition, isExpr, joinFragments, resolveDeferredConditions };
|
|
310
308
|
//# sourceMappingURL=Expr.d.cts.map
|
package/build/Expr.d.cts.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"Expr.d.cts","names":[],"sources":["../src/Expr.ts"],"mappings":"
|
|
1
|
+
{"version":3,"file":"Expr.d.cts","names":[],"sources":["../src/Expr.ts"],"mappings":";;;;;;;;;;;;;;;cAkBa;;;;;;;;;;;;;;;;;;;;;UAsBI,UAAU,GAAG;WACnB,UAAU;WACV,UAAU;;;;;;;;;;;;;;;;;;;;;;UAuBJ;EACf;EACA;EACA,SAAS;;EAET;;;;;;;;;;KAWU,SAAS;YAAiB,YAAY,UAAU;;;;;;;;;;UAU3C,KAAK,GAAG;YACb,YAAY,UAAU,GAAG;EACnC,GAAG,OAAO,IAAI,SAAS,iBAAiB;EACxC,GAAG,OAAO,IAAI,SAAS,iBAAiB;EACxC,GAAG,OAAO,IAAI,SAAS,iBAAiB;EACxC,IAAI,OAAO,IAAI,SAAS,iBAAiB;EACzC,GAAG,OAAO,IAAI,SAAS,iBAAiB;EACxC,IAAI,OAAO,IAAI,SAAS,iBAAiB;EAEzC,GAAG,iBAAiB,MAAM,SAAS,wBAAwB;EAC3D,IAAI,iBAAiB,MAAM,SAAS,wBAAwB;;EAG5D,SAAS;EACT,iBAAiB;EACjB,IAAI,MAAM,oBAAoB,OAAO,oBAAoB;EACzD,IAAI,MAAM,oBAAoB,OAAO,oBAAoB;EACzD,OAAO,KAAK,UAAU;EACtB,OAAO,KAAK,UAAU;;EAEtB,YAAY,KAAK,YAAY;EAC7B,UAAU,MAAM,oBAAoB,MAAM,oBAAoB,oBAAoB;EAClF,SAAS,UAAU,YAAY,KAAK,KAAK,YAAY;;;;;;;;;;;;;UActC,UAAU;WAChB,OAAO;WACP;WACA,IAAI;WACJ;;UAGM,SAAS;WACf,MAAM;WACN;WACA,IAAI;WACJ;;;UAIM;EACf;EACA;EACA;;;;;;UAOe;;EAEf,SAAS;;EAET,eAAe,MAAM,sBAAsB;;iBAG7B,OAAO,iBAAiB,SAAS;;iBAKjC,OAAO,MAAM,gBAAgB;cAIhC;;;;;;;;;UAUI,0BAA0B;GACxC,eAAe,KAAK,gBAAgB;;iBAGvB,oBAAoB,gBAAgB,QAAQ;;iBAK5C,kBAAkB,KAAK,KAAK,gBAAgB,cAAc;;;;;iBAmB1D,0BACd,MAAM,iCACN,KAAK,cACJ;;iBAaa,cAAc,OAAO,eAAe,cAAc;;;;;;;uBAc5C;YACV;;WAGD,MAAM,KAAK,cAAc;;MAG9B,kBAAkB;;MAKlB,cAAc;;MAKd;;MAKA;;EAKJ,UAAU,KAAK,cAAc;;EAK7B,OAAO;;EAKP,OAAO;EAIP,GAAG,iBAAiB;EAIpB,GAAG,iBAAiB;EAIpB,GAAG,iBAAiB;EAIpB,IAAI,iBAAiB;EAIrB,GAAG,iBAAiB;EAIpB,IAAI,iBAAiB;EAIrB,GAAG,kBAAkB;EAIrB,IAAI,kBAAkB;EAItB,SAAS;EAST,iBAAiB;EAUjB,OAAO;EAQP,OAAO;EAQP,OAAO;EAOP,OAAO;EAOP,YAAY;EAUZ,UAAU,oBAAoB;EAO9B,SAAS,oBAAoB;;YAQnB,QAAQ,YAAY,iBAAiB;;YAmBrC,OAAO,qBAAqB,kBAAkB;;;;;;;;;cA6B7C,eAAe;UAEhB;UACA;UACA;EAHV,YACU,cACA,MAAM,YACN;IACN;IACA;IACA,UAAU;IACV,UAAU;IACV,aAAa;;MAMb,cAAc;MAId;EAmBJ,MAAM,KAAK,cAAc;EAQzB,OAAO;EAKP,OAAO;;;cAMI,oBAAoB;UACX;EAApB,YAAoB;MAIhB;EAIJ,SAAS;;;cAME,gBAAgB;UAEjB;UACA;EAFV,YACU,gBACA;MAKN;EAIJ,MAAM,KAAK,cAAc;;;;;;;;;;;;;cAkBd,qBAAqB;UAEtB;UACA;EAFV,YACU,SAAS,sBACT;EAKV,MAAM,KAAK,cAAc;;;iBAWX,mBAAmB,gBAAgB,KAAK,cAAc;;iBAWtD,gBAAgB;;;;;;;;;;;;;;;;;;iBAuBhB,gBAAgB;;;;;;;;;iBA+DhB,gBAAgB,YAAY,6BAA6B"}
|
package/build/Expr.d.mts
CHANGED
|
@@ -1,4 +1,3 @@
|
|
|
1
|
-
import { AliasResolver, DeferredAliasCondition, deferredAliasSym, isDeferredAliasCondition, skipCondition, withDeferredAlias } from "./DeferredAlias.mjs";
|
|
2
1
|
import { EntityMetadata } from "./EntityMetadata.mjs";
|
|
3
2
|
import { ExpressionCondition } from "./EntityFilter.mjs";
|
|
4
3
|
import { RawCondition } from "./QueryParser.mjs";
|
|
@@ -141,7 +140,7 @@ interface SqlFragment {
|
|
|
141
140
|
* source, and a way to turn nested conditions into SQL (which needs `ConditionBuilder`, so it lives in `query.ts`).
|
|
142
141
|
*/
|
|
143
142
|
interface ExprContext {
|
|
144
|
-
/** Returns the SQL alias for
|
|
143
|
+
/** Returns the SQL alias for a table's `TableMgmt` or a subquery handle, searching enclosing queries. */
|
|
145
144
|
aliasFor(handle: object): string;
|
|
146
145
|
/** Turns a user-facing condition into SQL; `undefined` if it pruned away entirely. */
|
|
147
146
|
conditionToSql(cond: ExpressionCondition): SqlFragment | undefined;
|
|
@@ -155,9 +154,8 @@ declare const deferredSym: unique symbol;
|
|
|
155
154
|
* `bookStats.bookCount.gt(1)` or `bs.authorId.eq(a.id)`.
|
|
156
155
|
*
|
|
157
156
|
* It is shaped like a `RawCondition` so it can sit in any `ExpressionFilter`; `resolveDeferredConditions`
|
|
158
|
-
* snapshots `condition`, `bindings`, and `aliases` before the filter is parsed.
|
|
159
|
-
*
|
|
160
|
-
* involve a non-alias expression.
|
|
157
|
+
* snapshots `condition`, `bindings`, and `aliases` before the filter is parsed. Domain alias conditions
|
|
158
|
+
* use a separate protocol in the `em.find` parser.
|
|
161
159
|
*/
|
|
162
160
|
interface DeferredCondition extends RawCondition {
|
|
163
161
|
[deferredSym]: (ctx: ExprContext) => RawCondition;
|
|
@@ -175,8 +173,8 @@ declare function joinFragments(parts: SqlFragment[], sep: string): SqlFragment;
|
|
|
175
173
|
/**
|
|
176
174
|
* The methods every expression shares. Subclasses provide `toSql`, and usually `decode`/`encode`.
|
|
177
175
|
*
|
|
178
|
-
*
|
|
179
|
-
*
|
|
176
|
+
* Table columns can override comparisons to apply column-specific conversions. Domain aliases do not
|
|
177
|
+
* implement this protocol.
|
|
180
178
|
*/
|
|
181
179
|
declare abstract class BaseExpr {
|
|
182
180
|
readonly [exprBrand]: any;
|
|
@@ -260,7 +258,7 @@ declare class RefExpr extends BaseExpr {
|
|
|
260
258
|
/**
|
|
261
259
|
* A `sql` tagged template.
|
|
262
260
|
*
|
|
263
|
-
* For an Author
|
|
261
|
+
* For an Author table `a` assigned the SQL alias `a1`:
|
|
264
262
|
*
|
|
265
263
|
* ```ts
|
|
266
264
|
* sql`${a.age} * 2` // Expression: a1.age * 2
|
|
@@ -306,5 +304,5 @@ declare function canonicalDbType(dbType: string): string;
|
|
|
306
304
|
*/
|
|
307
305
|
declare function arrayOutputType(outputType: ExprOutputType | undefined): ExprOutputType | undefined;
|
|
308
306
|
//#endregion
|
|
309
|
-
export {
|
|
307
|
+
export { BaseExpr, BindingExpr, DeferredCondition, Expr, ExprBrand, ExprContext, ExprLike, ExprOutputType, FnExpr, InnerJoin, LeftJoin, RefExpr, SqlFragment, TemplateExpr, arrayOutputType, asNode, canonicalDbType, deferredCondition, deferredSym, exprBrand, interpolationToSql, isConditionLike, isDeferredCondition, isExpr, joinFragments, resolveDeferredConditions };
|
|
310
308
|
//# sourceMappingURL=Expr.d.mts.map
|
package/build/Expr.d.mts.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"Expr.d.mts","names":[],"sources":["../src/Expr.ts"],"mappings":"
|
|
1
|
+
{"version":3,"file":"Expr.d.mts","names":[],"sources":["../src/Expr.ts"],"mappings":";;;;;;;;;;;;;;;cAkBa;;;;;;;;;;;;;;;;;;;;;UAsBI,UAAU,GAAG;WACnB,UAAU;WACV,UAAU;;;;;;;;;;;;;;;;;;;;;;UAuBJ;EACf;EACA;EACA,SAAS;;EAET;;;;;;;;;;KAWU,SAAS;YAAiB,YAAY,UAAU;;;;;;;;;;UAU3C,KAAK,GAAG;YACb,YAAY,UAAU,GAAG;EACnC,GAAG,OAAO,IAAI,SAAS,iBAAiB;EACxC,GAAG,OAAO,IAAI,SAAS,iBAAiB;EACxC,GAAG,OAAO,IAAI,SAAS,iBAAiB;EACxC,IAAI,OAAO,IAAI,SAAS,iBAAiB;EACzC,GAAG,OAAO,IAAI,SAAS,iBAAiB;EACxC,IAAI,OAAO,IAAI,SAAS,iBAAiB;EAEzC,GAAG,iBAAiB,MAAM,SAAS,wBAAwB;EAC3D,IAAI,iBAAiB,MAAM,SAAS,wBAAwB;;EAG5D,SAAS;EACT,iBAAiB;EACjB,IAAI,MAAM,oBAAoB,OAAO,oBAAoB;EACzD,IAAI,MAAM,oBAAoB,OAAO,oBAAoB;EACzD,OAAO,KAAK,UAAU;EACtB,OAAO,KAAK,UAAU;;EAEtB,YAAY,KAAK,YAAY;EAC7B,UAAU,MAAM,oBAAoB,MAAM,oBAAoB,oBAAoB;EAClF,SAAS,UAAU,YAAY,KAAK,KAAK,YAAY;;;;;;;;;;;;;UActC,UAAU;WAChB,OAAO;WACP;WACA,IAAI;WACJ;;UAGM,SAAS;WACf,MAAM;WACN;WACA,IAAI;WACJ;;;UAIM;EACf;EACA;EACA;;;;;;UAOe;;EAEf,SAAS;;EAET,eAAe,MAAM,sBAAsB;;iBAG7B,OAAO,iBAAiB,SAAS;;iBAKjC,OAAO,MAAM,gBAAgB;cAIhC;;;;;;;;;UAUI,0BAA0B;GACxC,eAAe,KAAK,gBAAgB;;iBAGvB,oBAAoB,gBAAgB,QAAQ;;iBAK5C,kBAAkB,KAAK,KAAK,gBAAgB,cAAc;;;;;iBAmB1D,0BACd,MAAM,iCACN,KAAK,cACJ;;iBAaa,cAAc,OAAO,eAAe,cAAc;;;;;;;uBAc5C;YACV;;WAGD,MAAM,KAAK,cAAc;;MAG9B,kBAAkB;;MAKlB,cAAc;;MAKd;;MAKA;;EAKJ,UAAU,KAAK,cAAc;;EAK7B,OAAO;;EAKP,OAAO;EAIP,GAAG,iBAAiB;EAIpB,GAAG,iBAAiB;EAIpB,GAAG,iBAAiB;EAIpB,IAAI,iBAAiB;EAIrB,GAAG,iBAAiB;EAIpB,IAAI,iBAAiB;EAIrB,GAAG,kBAAkB;EAIrB,IAAI,kBAAkB;EAItB,SAAS;EAST,iBAAiB;EAUjB,OAAO;EAQP,OAAO;EAQP,OAAO;EAOP,OAAO;EAOP,YAAY;EAUZ,UAAU,oBAAoB;EAO9B,SAAS,oBAAoB;;YAQnB,QAAQ,YAAY,iBAAiB;;YAmBrC,OAAO,qBAAqB,kBAAkB;;;;;;;;;cA6B7C,eAAe;UAEhB;UACA;UACA;EAHV,YACU,cACA,MAAM,YACN;IACN;IACA;IACA,UAAU;IACV,UAAU;IACV,aAAa;;MAMb,cAAc;MAId;EAmBJ,MAAM,KAAK,cAAc;EAQzB,OAAO;EAKP,OAAO;;;cAMI,oBAAoB;UACX;EAApB,YAAoB;MAIhB;EAIJ,SAAS;;;cAME,gBAAgB;UAEjB;UACA;EAFV,YACU,gBACA;MAKN;EAIJ,MAAM,KAAK,cAAc;;;;;;;;;;;;;cAkBd,qBAAqB;UAEtB;UACA;EAFV,YACU,SAAS,sBACT;EAKV,MAAM,KAAK,cAAc;;;iBAWX,mBAAmB,gBAAgB,KAAK,cAAc;;iBAWtD,gBAAgB;;;;;;;;;;;;;;;;;;iBAuBhB,gBAAgB;;;;;;;;;iBA+DhB,gBAAgB,YAAY,6BAA6B"}
|
package/build/Expr.js
CHANGED
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
import { safeKq } from "./keywords.js";
|
|
2
|
-
import {
|
|
2
|
+
import { skipCondition } from "./skipCondition.js";
|
|
3
3
|
//#region src/Expr.ts
|
|
4
4
|
/**
|
|
5
5
|
* The shared expression protocol for `em.query`.
|
|
@@ -51,7 +51,6 @@ function deferredCondition(fn) {
|
|
|
51
51
|
function resolveDeferredConditions(cond, ctx) {
|
|
52
52
|
if (cond === void 0 || cond === null) return cond;
|
|
53
53
|
if (isDeferredCondition(cond)) return cond[deferredSym](ctx);
|
|
54
|
-
else if (isDeferredAliasCondition(cond)) return cond[deferredAliasSym](ctxResolver(ctx));
|
|
55
54
|
else if ("and" in cond && cond.and) return {
|
|
56
55
|
...cond,
|
|
57
56
|
and: cond.and.map((c) => resolveDeferredConditions(c, ctx))
|
|
@@ -73,8 +72,8 @@ function joinFragments(parts, sep) {
|
|
|
73
72
|
/**
|
|
74
73
|
* The methods every expression shares. Subclasses provide `toSql`, and usually `decode`/`encode`.
|
|
75
74
|
*
|
|
76
|
-
*
|
|
77
|
-
*
|
|
75
|
+
* Table columns can override comparisons to apply column-specific conversions. Domain aliases do not
|
|
76
|
+
* implement this protocol.
|
|
78
77
|
*/
|
|
79
78
|
var BaseExpr = class {
|
|
80
79
|
[exprBrand] = this;
|
|
@@ -329,7 +328,7 @@ var RefExpr = class extends BaseExpr {
|
|
|
329
328
|
/**
|
|
330
329
|
* A `sql` tagged template.
|
|
331
330
|
*
|
|
332
|
-
* For an Author
|
|
331
|
+
* For an Author table `a` assigned the SQL alias `a1`:
|
|
333
332
|
*
|
|
334
333
|
* ```ts
|
|
335
334
|
* sql`${a.age} * 2` // Expression: a1.age * 2
|
|
@@ -511,15 +510,6 @@ function minMaxOutputType(outputType) {
|
|
|
511
510
|
default: return;
|
|
512
511
|
}
|
|
513
512
|
}
|
|
514
|
-
/** An `AliasResolver` backed by an `ExprContext`; a handle's bound meta is its own (`em.query` sources are their own tables). */
|
|
515
|
-
function ctxResolver(ctx) {
|
|
516
|
-
return function resolve(handle) {
|
|
517
|
-
return {
|
|
518
|
-
meta: handle.meta,
|
|
519
|
-
alias: ctx.aliasFor(handle)
|
|
520
|
-
};
|
|
521
|
-
};
|
|
522
|
-
}
|
|
523
513
|
/** Decodes `count`/`sum`/`avg` results, which Postgres returns as strings for bigint/numeric. */
|
|
524
514
|
function decodeNumber(value) {
|
|
525
515
|
return typeof value === "string" ? Number(value) : value;
|
|
@@ -531,6 +521,6 @@ function fail(message) {
|
|
|
531
521
|
throw new Error(message);
|
|
532
522
|
}
|
|
533
523
|
//#endregion
|
|
534
|
-
export { BaseExpr, BindingExpr, FnExpr, RefExpr, TemplateExpr, arrayOutputType, asNode, canonicalDbType,
|
|
524
|
+
export { BaseExpr, BindingExpr, FnExpr, RefExpr, TemplateExpr, arrayOutputType, asNode, canonicalDbType, deferredCondition, deferredSym, exprBrand, interpolationToSql, isConditionLike, isDeferredCondition, isExpr, joinFragments, resolveDeferredConditions };
|
|
535
525
|
|
|
536
526
|
//# sourceMappingURL=Expr.js.map
|