joist-core 2.3.0-next.92 → 2.3.0-next.93
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/expressions/arrayAgg.cjs +63 -0
- package/build/expressions/arrayAgg.cjs.map +1 -0
- package/build/expressions/arrayAgg.d.cts +24 -0
- package/build/expressions/arrayAgg.d.cts.map +1 -0
- package/build/expressions/arrayAgg.d.mts +24 -0
- package/build/expressions/arrayAgg.d.mts.map +1 -0
- package/build/expressions/arrayAgg.js +61 -0
- package/build/expressions/arrayAgg.js.map +1 -0
- package/build/expressions/codecs.cjs +14 -5
- package/build/expressions/codecs.cjs.map +1 -1
- package/build/expressions/codecs.js +14 -5
- package/build/expressions/codecs.js.map +1 -1
- package/build/expressions/expression.cjs +4 -1
- package/build/expressions/expression.cjs.map +1 -1
- package/build/expressions/expression.d.cts.map +1 -1
- package/build/expressions/expression.d.mts.map +1 -1
- package/build/expressions/expression.js +4 -1
- package/build/expressions/expression.js.map +1 -1
- package/build/expressions/parseExpression.cjs +7 -2
- package/build/expressions/parseExpression.cjs.map +1 -1
- package/build/expressions/parseExpression.d.cts.map +1 -1
- package/build/expressions/parseExpression.d.mts.map +1 -1
- package/build/expressions/parseExpression.js +7 -2
- package/build/expressions/parseExpression.js.map +1 -1
- package/build/expressions/types.d.cts +26 -6
- package/build/expressions/types.d.cts.map +1 -1
- package/build/expressions/types.d.mts +26 -6
- package/build/expressions/types.d.mts.map +1 -1
- package/package.json +2 -2
|
@@ -0,0 +1,63 @@
|
|
|
1
|
+
Object.defineProperty(exports, Symbol.toStringTag, { value: "Module" });
|
|
2
|
+
const require_Expr = require("../Expr.cjs");
|
|
3
|
+
const require_expressions_parseExpression = require("./parseExpression.cjs");
|
|
4
|
+
const require_expressions_expression = require("./expression.cjs");
|
|
5
|
+
//#region src/expressions/arrayAgg.ts
|
|
6
|
+
/** Parses compact and expanded ARRAY_AGG inputs. */
|
|
7
|
+
function parseArrayAggExpression(input) {
|
|
8
|
+
if (input instanceof require_Expr.BaseExpr || !isArrayAggOptions(input)) return {
|
|
9
|
+
kind: "arrayAgg",
|
|
10
|
+
value: require_expressions_parseExpression.parseExpression(input)
|
|
11
|
+
};
|
|
12
|
+
require_expressions_parseExpression.checkKeys(input, [
|
|
13
|
+
"value",
|
|
14
|
+
"distinct",
|
|
15
|
+
"orderBy",
|
|
16
|
+
"filter"
|
|
17
|
+
]);
|
|
18
|
+
if (!("value" in input)) throw new Error("ARRAY_AGG options need a value");
|
|
19
|
+
if (input.distinct !== void 0 && typeof input.distinct !== "boolean") throw new Error("ARRAY_AGG distinct must be a boolean");
|
|
20
|
+
if (input.orderBy !== void 0 && !Array.isArray(input.orderBy)) throw new Error("ARRAY_AGG orderBy must be an array");
|
|
21
|
+
return {
|
|
22
|
+
kind: "arrayAgg",
|
|
23
|
+
value: require_expressions_parseExpression.parseExpression(input.value),
|
|
24
|
+
options: {
|
|
25
|
+
distinct: input.distinct,
|
|
26
|
+
orderBy: input.orderBy,
|
|
27
|
+
filter: input.filter
|
|
28
|
+
}
|
|
29
|
+
};
|
|
30
|
+
}
|
|
31
|
+
/** Renders ARRAY_AGG's value, ordering, and filter in SQL binding order. */
|
|
32
|
+
function arrayAggToSql(parsed, ctx, valueCodec) {
|
|
33
|
+
const value = require_expressions_expression.expressionToSql(parsed.value, ctx, valueCodec);
|
|
34
|
+
const ordering = require_Expr.joinFragments((parsed.options?.orderBy ?? []).flatMap((entry) => {
|
|
35
|
+
if (entry === void 0) return [];
|
|
36
|
+
const fragment = require_Expr.orderByToSql(entry, ctx);
|
|
37
|
+
return fragment ? [fragment] : [];
|
|
38
|
+
}), ", ");
|
|
39
|
+
const filter = parsed.options?.filter;
|
|
40
|
+
const condition = filter === void 0 ? void 0 : ctx.conditionToSql(filter);
|
|
41
|
+
return {
|
|
42
|
+
sql: `array_agg(${parsed.options?.distinct ? "DISTINCT " : ""}${value.sql}${ordering.sql ? ` ORDER BY ${ordering.sql}` : ""})${condition ? ` FILTER (WHERE ${condition.sql})` : ""}`,
|
|
43
|
+
bindings: [
|
|
44
|
+
...value.bindings,
|
|
45
|
+
...ordering.bindings,
|
|
46
|
+
...condition?.bindings ?? []
|
|
47
|
+
],
|
|
48
|
+
refs: [
|
|
49
|
+
...value.refs,
|
|
50
|
+
...ordering.refs,
|
|
51
|
+
...condition?.refs ?? []
|
|
52
|
+
]
|
|
53
|
+
};
|
|
54
|
+
}
|
|
55
|
+
/** Distinguishes expanded options from nested object expressions. */
|
|
56
|
+
function isArrayAggOptions(input) {
|
|
57
|
+
return require_expressions_parseExpression.isObject(input) && ("value" in input || "distinct" in input || "orderBy" in input || "filter" in input);
|
|
58
|
+
}
|
|
59
|
+
//#endregion
|
|
60
|
+
exports.arrayAggToSql = arrayAggToSql;
|
|
61
|
+
exports.parseArrayAggExpression = parseArrayAggExpression;
|
|
62
|
+
|
|
63
|
+
//# sourceMappingURL=arrayAgg.cjs.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"arrayAgg.cjs","names":["BaseExpr","parseExpression","expressionToSql","joinFragments","orderByToSql","isObject"],"sources":["../../src/expressions/arrayAgg.ts"],"sourcesContent":["import {\n type ArrayAggOptions,\n BaseExpr,\n type ExprContext,\n type ExprLike,\n type SqlFragment,\n joinFragments,\n orderByToSql,\n} from \"../Expr.ts\";\nimport { expressionToSql } from \"./expression.ts\";\nimport { checkKeys, isObject, parseExpression } from \"./parseExpression.ts\";\nimport type { ParsedExpression, ResultCodec } from \"./types.ts\";\n\n/** The aggregate value alone, or the value together with PostgreSQL aggregate options. */\nexport interface ArrayAggInput {\n readonly arrayAgg: ExprLike<unknown> | ArrayAggExpressionOptions;\n}\n\n/** Options for an inline ARRAY_AGG expression. */\nexport interface ArrayAggExpressionOptions extends ArrayAggOptions {\n readonly value: unknown;\n}\n\n/** An ARRAY_AGG value and its aggregate options. */\nexport interface ParsedArrayAggExpression {\n kind: \"arrayAgg\";\n value: ParsedExpression;\n options?: ArrayAggOptions;\n}\n\n/** Parses compact and expanded ARRAY_AGG inputs. */\nexport function parseArrayAggExpression(input: unknown): ParsedArrayAggExpression {\n if (input instanceof BaseExpr || !isArrayAggOptions(input)) {\n return { kind: \"arrayAgg\", value: parseExpression(input) };\n }\n checkKeys(input, [\"value\", \"distinct\", \"orderBy\", \"filter\"]);\n if (!(\"value\" in input)) throw new Error(\"ARRAY_AGG options need a value\");\n if (input.distinct !== undefined && typeof input.distinct !== \"boolean\") {\n throw new Error(\"ARRAY_AGG distinct must be a boolean\");\n }\n if (input.orderBy !== undefined && !Array.isArray(input.orderBy)) {\n throw new Error(\"ARRAY_AGG orderBy must be an array\");\n }\n return {\n kind: \"arrayAgg\",\n value: parseExpression(input.value),\n options: {\n distinct: input.distinct as boolean | undefined,\n orderBy: input.orderBy as ArrayAggOptions[\"orderBy\"],\n filter: input.filter as ArrayAggOptions[\"filter\"],\n },\n };\n}\n\n/** Renders ARRAY_AGG's value, ordering, and filter in SQL binding order. */\nexport function arrayAggToSql(\n parsed: ParsedArrayAggExpression,\n ctx: ExprContext,\n valueCodec: ResultCodec,\n): SqlFragment {\n const value = expressionToSql(parsed.value, ctx, valueCodec);\n const ordering = joinFragments(\n (parsed.options?.orderBy ?? []).flatMap((entry) => {\n if (entry === undefined) return [];\n const fragment = orderByToSql(entry, ctx);\n return fragment ? [fragment] : [];\n }),\n \", \",\n );\n const filter = parsed.options?.filter;\n const condition = filter === undefined ? undefined : ctx.conditionToSql(filter);\n return {\n sql: `array_agg(${parsed.options?.distinct ? \"DISTINCT \" : \"\"}${value.sql}${ordering.sql ? ` ORDER BY ${ordering.sql}` : \"\"})${condition ? ` FILTER (WHERE ${condition.sql})` : \"\"}`,\n bindings: [...value.bindings, ...ordering.bindings, ...(condition?.bindings ?? [])],\n refs: [...value.refs, ...ordering.refs, ...(condition?.refs ?? [])],\n };\n}\n\n/** Distinguishes expanded options from nested object expressions. */\nfunction isArrayAggOptions(input: unknown): input is Record<string, unknown> {\n return isObject(input) && (\"value\" in input || \"distinct\" in input || \"orderBy\" in input || \"filter\" in input);\n}\n"],"mappings":";;;;;;AA+BA,SAAgB,wBAAwB,OAA0C;CAChF,IAAI,iBAAiBA,aAAAA,YAAY,CAAC,kBAAkB,KAAK,GACvD,OAAO;EAAE,MAAM;EAAY,OAAOC,oCAAAA,gBAAgB,KAAK;CAAE;CAE3D,oCAAA,UAAU,OAAO;EAAC;EAAS;EAAY;EAAW;CAAQ,CAAC;CAC3D,IAAI,EAAE,WAAW,QAAQ,MAAM,IAAI,MAAM,gCAAgC;CACzE,IAAI,MAAM,aAAa,KAAA,KAAa,OAAO,MAAM,aAAa,WAC5D,MAAM,IAAI,MAAM,sCAAsC;CAExD,IAAI,MAAM,YAAY,KAAA,KAAa,CAAC,MAAM,QAAQ,MAAM,OAAO,GAC7D,MAAM,IAAI,MAAM,oCAAoC;CAEtD,OAAO;EACL,MAAM;EACN,OAAOA,oCAAAA,gBAAgB,MAAM,KAAK;EAClC,SAAS;GACP,UAAU,MAAM;GAChB,SAAS,MAAM;GACf,QAAQ,MAAM;EAChB;CACF;AACF;;AAGA,SAAgB,cACd,QACA,KACA,YACa;CACb,MAAM,QAAQC,+BAAAA,gBAAgB,OAAO,OAAO,KAAK,UAAU;CAC3D,MAAM,WAAWC,aAAAA,eACd,OAAO,SAAS,WAAW,CAAC,EAAA,CAAG,SAAS,UAAU;EACjD,IAAI,UAAU,KAAA,GAAW,OAAO,CAAC;EACjC,MAAM,WAAWC,aAAAA,aAAa,OAAO,GAAG;EACxC,OAAO,WAAW,CAAC,QAAQ,IAAI,CAAC;CAClC,CAAC,GACD,IACF;CACA,MAAM,SAAS,OAAO,SAAS;CAC/B,MAAM,YAAY,WAAW,KAAA,IAAY,KAAA,IAAY,IAAI,eAAe,MAAM;CAC9E,OAAO;EACL,KAAK,aAAa,OAAO,SAAS,WAAW,cAAc,KAAK,MAAM,MAAM,SAAS,MAAM,aAAa,SAAS,QAAQ,GAAG,GAAG,YAAY,kBAAkB,UAAU,IAAI,KAAK;EAChL,UAAU;GAAC,GAAG,MAAM;GAAU,GAAG,SAAS;GAAU,GAAI,WAAW,YAAY,CAAC;EAAE;EAClF,MAAM;GAAC,GAAG,MAAM;GAAM,GAAG,SAAS;GAAM,GAAI,WAAW,QAAQ,CAAC;EAAE;CACpE;AACF;;AAGA,SAAS,kBAAkB,OAAkD;CAC3E,OAAOC,oCAAAA,SAAS,KAAK,MAAM,WAAW,SAAS,cAAc,SAAS,aAAa,SAAS,YAAY;AAC1G"}
|
|
@@ -0,0 +1,24 @@
|
|
|
1
|
+
import { ParsedExpression, ResultCodec } from "./types.cjs";
|
|
2
|
+
import { ArrayAggOptions, ExprContext, ExprLike, SqlFragment } from "../Expr.cjs";
|
|
3
|
+
//#region src/expressions/arrayAgg.d.ts
|
|
4
|
+
/** The aggregate value alone, or the value together with PostgreSQL aggregate options. */
|
|
5
|
+
interface ArrayAggInput {
|
|
6
|
+
readonly arrayAgg: ExprLike<unknown> | ArrayAggExpressionOptions;
|
|
7
|
+
}
|
|
8
|
+
/** Options for an inline ARRAY_AGG expression. */
|
|
9
|
+
interface ArrayAggExpressionOptions extends ArrayAggOptions {
|
|
10
|
+
readonly value: unknown;
|
|
11
|
+
}
|
|
12
|
+
/** An ARRAY_AGG value and its aggregate options. */
|
|
13
|
+
interface ParsedArrayAggExpression {
|
|
14
|
+
kind: "arrayAgg";
|
|
15
|
+
value: ParsedExpression;
|
|
16
|
+
options?: ArrayAggOptions;
|
|
17
|
+
}
|
|
18
|
+
/** Parses compact and expanded ARRAY_AGG inputs. */
|
|
19
|
+
declare function parseArrayAggExpression(input: unknown): ParsedArrayAggExpression;
|
|
20
|
+
/** Renders ARRAY_AGG's value, ordering, and filter in SQL binding order. */
|
|
21
|
+
declare function arrayAggToSql(parsed: ParsedArrayAggExpression, ctx: ExprContext, valueCodec: ResultCodec): SqlFragment;
|
|
22
|
+
//#endregion
|
|
23
|
+
export { ArrayAggExpressionOptions, ArrayAggInput, ParsedArrayAggExpression, arrayAggToSql, parseArrayAggExpression };
|
|
24
|
+
//# sourceMappingURL=arrayAgg.d.cts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"arrayAgg.d.cts","names":[],"sources":["../../src/expressions/arrayAgg.ts"],"mappings":";;;;UAciB;WACN,UAAU,oBAAoB;;;UAIxB,kCAAkC;WACxC;;;UAIM;EACf;EACA,OAAO;EACP,UAAU;;;iBAII,wBAAwB,iBAAiB;;iBAwBzC,cACd,QAAQ,0BACR,KAAK,aACL,YAAY,cACX"}
|
|
@@ -0,0 +1,24 @@
|
|
|
1
|
+
import { ParsedExpression, ResultCodec } from "./types.mjs";
|
|
2
|
+
import { ArrayAggOptions, ExprContext, ExprLike, SqlFragment } from "../Expr.mjs";
|
|
3
|
+
//#region src/expressions/arrayAgg.d.ts
|
|
4
|
+
/** The aggregate value alone, or the value together with PostgreSQL aggregate options. */
|
|
5
|
+
interface ArrayAggInput {
|
|
6
|
+
readonly arrayAgg: ExprLike<unknown> | ArrayAggExpressionOptions;
|
|
7
|
+
}
|
|
8
|
+
/** Options for an inline ARRAY_AGG expression. */
|
|
9
|
+
interface ArrayAggExpressionOptions extends ArrayAggOptions {
|
|
10
|
+
readonly value: unknown;
|
|
11
|
+
}
|
|
12
|
+
/** An ARRAY_AGG value and its aggregate options. */
|
|
13
|
+
interface ParsedArrayAggExpression {
|
|
14
|
+
kind: "arrayAgg";
|
|
15
|
+
value: ParsedExpression;
|
|
16
|
+
options?: ArrayAggOptions;
|
|
17
|
+
}
|
|
18
|
+
/** Parses compact and expanded ARRAY_AGG inputs. */
|
|
19
|
+
declare function parseArrayAggExpression(input: unknown): ParsedArrayAggExpression;
|
|
20
|
+
/** Renders ARRAY_AGG's value, ordering, and filter in SQL binding order. */
|
|
21
|
+
declare function arrayAggToSql(parsed: ParsedArrayAggExpression, ctx: ExprContext, valueCodec: ResultCodec): SqlFragment;
|
|
22
|
+
//#endregion
|
|
23
|
+
export { ArrayAggExpressionOptions, ArrayAggInput, ParsedArrayAggExpression, arrayAggToSql, parseArrayAggExpression };
|
|
24
|
+
//# sourceMappingURL=arrayAgg.d.mts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"arrayAgg.d.mts","names":[],"sources":["../../src/expressions/arrayAgg.ts"],"mappings":";;;;UAciB;WACN,UAAU,oBAAoB;;;UAIxB,kCAAkC;WACxC;;;UAIM;EACf;EACA,OAAO;EACP,UAAU;;;iBAII,wBAAwB,iBAAiB;;iBAwBzC,cACd,QAAQ,0BACR,KAAK,aACL,YAAY,cACX"}
|
|
@@ -0,0 +1,61 @@
|
|
|
1
|
+
import { BaseExpr, joinFragments, orderByToSql } from "../Expr.js";
|
|
2
|
+
import { checkKeys, isObject, parseExpression } from "./parseExpression.js";
|
|
3
|
+
import { expressionToSql } from "./expression.js";
|
|
4
|
+
//#region src/expressions/arrayAgg.ts
|
|
5
|
+
/** Parses compact and expanded ARRAY_AGG inputs. */
|
|
6
|
+
function parseArrayAggExpression(input) {
|
|
7
|
+
if (input instanceof BaseExpr || !isArrayAggOptions(input)) return {
|
|
8
|
+
kind: "arrayAgg",
|
|
9
|
+
value: parseExpression(input)
|
|
10
|
+
};
|
|
11
|
+
checkKeys(input, [
|
|
12
|
+
"value",
|
|
13
|
+
"distinct",
|
|
14
|
+
"orderBy",
|
|
15
|
+
"filter"
|
|
16
|
+
]);
|
|
17
|
+
if (!("value" in input)) throw new Error("ARRAY_AGG options need a value");
|
|
18
|
+
if (input.distinct !== void 0 && typeof input.distinct !== "boolean") throw new Error("ARRAY_AGG distinct must be a boolean");
|
|
19
|
+
if (input.orderBy !== void 0 && !Array.isArray(input.orderBy)) throw new Error("ARRAY_AGG orderBy must be an array");
|
|
20
|
+
return {
|
|
21
|
+
kind: "arrayAgg",
|
|
22
|
+
value: parseExpression(input.value),
|
|
23
|
+
options: {
|
|
24
|
+
distinct: input.distinct,
|
|
25
|
+
orderBy: input.orderBy,
|
|
26
|
+
filter: input.filter
|
|
27
|
+
}
|
|
28
|
+
};
|
|
29
|
+
}
|
|
30
|
+
/** Renders ARRAY_AGG's value, ordering, and filter in SQL binding order. */
|
|
31
|
+
function arrayAggToSql(parsed, ctx, valueCodec) {
|
|
32
|
+
const value = expressionToSql(parsed.value, ctx, valueCodec);
|
|
33
|
+
const ordering = joinFragments((parsed.options?.orderBy ?? []).flatMap((entry) => {
|
|
34
|
+
if (entry === void 0) return [];
|
|
35
|
+
const fragment = orderByToSql(entry, ctx);
|
|
36
|
+
return fragment ? [fragment] : [];
|
|
37
|
+
}), ", ");
|
|
38
|
+
const filter = parsed.options?.filter;
|
|
39
|
+
const condition = filter === void 0 ? void 0 : ctx.conditionToSql(filter);
|
|
40
|
+
return {
|
|
41
|
+
sql: `array_agg(${parsed.options?.distinct ? "DISTINCT " : ""}${value.sql}${ordering.sql ? ` ORDER BY ${ordering.sql}` : ""})${condition ? ` FILTER (WHERE ${condition.sql})` : ""}`,
|
|
42
|
+
bindings: [
|
|
43
|
+
...value.bindings,
|
|
44
|
+
...ordering.bindings,
|
|
45
|
+
...condition?.bindings ?? []
|
|
46
|
+
],
|
|
47
|
+
refs: [
|
|
48
|
+
...value.refs,
|
|
49
|
+
...ordering.refs,
|
|
50
|
+
...condition?.refs ?? []
|
|
51
|
+
]
|
|
52
|
+
};
|
|
53
|
+
}
|
|
54
|
+
/** Distinguishes expanded options from nested object expressions. */
|
|
55
|
+
function isArrayAggOptions(input) {
|
|
56
|
+
return isObject(input) && ("value" in input || "distinct" in input || "orderBy" in input || "filter" in input);
|
|
57
|
+
}
|
|
58
|
+
//#endregion
|
|
59
|
+
export { arrayAggToSql, parseArrayAggExpression };
|
|
60
|
+
|
|
61
|
+
//# sourceMappingURL=arrayAgg.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"arrayAgg.js","names":[],"sources":["../../src/expressions/arrayAgg.ts"],"sourcesContent":["import {\n type ArrayAggOptions,\n BaseExpr,\n type ExprContext,\n type ExprLike,\n type SqlFragment,\n joinFragments,\n orderByToSql,\n} from \"../Expr.ts\";\nimport { expressionToSql } from \"./expression.ts\";\nimport { checkKeys, isObject, parseExpression } from \"./parseExpression.ts\";\nimport type { ParsedExpression, ResultCodec } from \"./types.ts\";\n\n/** The aggregate value alone, or the value together with PostgreSQL aggregate options. */\nexport interface ArrayAggInput {\n readonly arrayAgg: ExprLike<unknown> | ArrayAggExpressionOptions;\n}\n\n/** Options for an inline ARRAY_AGG expression. */\nexport interface ArrayAggExpressionOptions extends ArrayAggOptions {\n readonly value: unknown;\n}\n\n/** An ARRAY_AGG value and its aggregate options. */\nexport interface ParsedArrayAggExpression {\n kind: \"arrayAgg\";\n value: ParsedExpression;\n options?: ArrayAggOptions;\n}\n\n/** Parses compact and expanded ARRAY_AGG inputs. */\nexport function parseArrayAggExpression(input: unknown): ParsedArrayAggExpression {\n if (input instanceof BaseExpr || !isArrayAggOptions(input)) {\n return { kind: \"arrayAgg\", value: parseExpression(input) };\n }\n checkKeys(input, [\"value\", \"distinct\", \"orderBy\", \"filter\"]);\n if (!(\"value\" in input)) throw new Error(\"ARRAY_AGG options need a value\");\n if (input.distinct !== undefined && typeof input.distinct !== \"boolean\") {\n throw new Error(\"ARRAY_AGG distinct must be a boolean\");\n }\n if (input.orderBy !== undefined && !Array.isArray(input.orderBy)) {\n throw new Error(\"ARRAY_AGG orderBy must be an array\");\n }\n return {\n kind: \"arrayAgg\",\n value: parseExpression(input.value),\n options: {\n distinct: input.distinct as boolean | undefined,\n orderBy: input.orderBy as ArrayAggOptions[\"orderBy\"],\n filter: input.filter as ArrayAggOptions[\"filter\"],\n },\n };\n}\n\n/** Renders ARRAY_AGG's value, ordering, and filter in SQL binding order. */\nexport function arrayAggToSql(\n parsed: ParsedArrayAggExpression,\n ctx: ExprContext,\n valueCodec: ResultCodec,\n): SqlFragment {\n const value = expressionToSql(parsed.value, ctx, valueCodec);\n const ordering = joinFragments(\n (parsed.options?.orderBy ?? []).flatMap((entry) => {\n if (entry === undefined) return [];\n const fragment = orderByToSql(entry, ctx);\n return fragment ? [fragment] : [];\n }),\n \", \",\n );\n const filter = parsed.options?.filter;\n const condition = filter === undefined ? undefined : ctx.conditionToSql(filter);\n return {\n sql: `array_agg(${parsed.options?.distinct ? \"DISTINCT \" : \"\"}${value.sql}${ordering.sql ? ` ORDER BY ${ordering.sql}` : \"\"})${condition ? ` FILTER (WHERE ${condition.sql})` : \"\"}`,\n bindings: [...value.bindings, ...ordering.bindings, ...(condition?.bindings ?? [])],\n refs: [...value.refs, ...ordering.refs, ...(condition?.refs ?? [])],\n };\n}\n\n/** Distinguishes expanded options from nested object expressions. */\nfunction isArrayAggOptions(input: unknown): input is Record<string, unknown> {\n return isObject(input) && (\"value\" in input || \"distinct\" in input || \"orderBy\" in input || \"filter\" in input);\n}\n"],"mappings":";;;;;AA+BA,SAAgB,wBAAwB,OAA0C;CAChF,IAAI,iBAAiB,YAAY,CAAC,kBAAkB,KAAK,GACvD,OAAO;EAAE,MAAM;EAAY,OAAO,gBAAgB,KAAK;CAAE;CAE3D,UAAU,OAAO;EAAC;EAAS;EAAY;EAAW;CAAQ,CAAC;CAC3D,IAAI,EAAE,WAAW,QAAQ,MAAM,IAAI,MAAM,gCAAgC;CACzE,IAAI,MAAM,aAAa,KAAA,KAAa,OAAO,MAAM,aAAa,WAC5D,MAAM,IAAI,MAAM,sCAAsC;CAExD,IAAI,MAAM,YAAY,KAAA,KAAa,CAAC,MAAM,QAAQ,MAAM,OAAO,GAC7D,MAAM,IAAI,MAAM,oCAAoC;CAEtD,OAAO;EACL,MAAM;EACN,OAAO,gBAAgB,MAAM,KAAK;EAClC,SAAS;GACP,UAAU,MAAM;GAChB,SAAS,MAAM;GACf,QAAQ,MAAM;EAChB;CACF;AACF;;AAGA,SAAgB,cACd,QACA,KACA,YACa;CACb,MAAM,QAAQ,gBAAgB,OAAO,OAAO,KAAK,UAAU;CAC3D,MAAM,WAAW,eACd,OAAO,SAAS,WAAW,CAAC,EAAA,CAAG,SAAS,UAAU;EACjD,IAAI,UAAU,KAAA,GAAW,OAAO,CAAC;EACjC,MAAM,WAAW,aAAa,OAAO,GAAG;EACxC,OAAO,WAAW,CAAC,QAAQ,IAAI,CAAC;CAClC,CAAC,GACD,IACF;CACA,MAAM,SAAS,OAAO,SAAS;CAC/B,MAAM,YAAY,WAAW,KAAA,IAAY,KAAA,IAAY,IAAI,eAAe,MAAM;CAC9E,OAAO;EACL,KAAK,aAAa,OAAO,SAAS,WAAW,cAAc,KAAK,MAAM,MAAM,SAAS,MAAM,aAAa,SAAS,QAAQ,GAAG,GAAG,YAAY,kBAAkB,UAAU,IAAI,KAAK;EAChL,UAAU;GAAC,GAAG,MAAM;GAAU,GAAG,SAAS;GAAU,GAAI,WAAW,YAAY,CAAC;EAAE;EAClF,MAAM;GAAC,GAAG,MAAM;GAAM,GAAG,SAAS;GAAM,GAAI,WAAW,QAAQ,CAAC;EAAE;CACpE;AACF;;AAGA,SAAS,kBAAkB,OAAkD;CAC3E,OAAO,SAAS,KAAK,MAAM,WAAW,SAAS,cAAc,SAAS,aAAa,SAAS,YAAY;AAC1G"}
|
|
@@ -1,5 +1,6 @@
|
|
|
1
1
|
Object.defineProperty(exports, Symbol.toStringTag, { value: "Module" });
|
|
2
2
|
const require_utils = require("../utils.cjs");
|
|
3
|
+
const require_TypeInfo = require("../TypeInfo.cjs");
|
|
3
4
|
const require_Expr = require("../Expr.cjs");
|
|
4
5
|
let node_util = require("node:util");
|
|
5
6
|
//#region src/expressions/codecs.ts
|
|
@@ -10,7 +11,7 @@ let node_util = require("node:util");
|
|
|
10
11
|
*/
|
|
11
12
|
function chooseExpressionCodec(parsed) {
|
|
12
13
|
const leaves = expressionLeaves(parsed);
|
|
13
|
-
const expressions = leaves.
|
|
14
|
+
const expressions = leaves.flatMap((leaf) => "codec" in leaf ? [leaf.codec] : []);
|
|
14
15
|
const first = expressions[0];
|
|
15
16
|
if (first) {
|
|
16
17
|
for (const other of expressions.slice(1)) {
|
|
@@ -26,10 +27,10 @@ function chooseExpressionCodec(parsed) {
|
|
|
26
27
|
throw new Error(`Expression operands need matching SQL types and codecs: ${describeType(a)} vs ${describeType(b)}; mismatched ${mismatches.join(", ")}`);
|
|
27
28
|
}
|
|
28
29
|
}
|
|
29
|
-
for (const leaf of leaves) if (
|
|
30
|
+
for (const leaf of leaves) if ("literal" in leaf) checkLiteral(leaf.literal.value, first.outputType);
|
|
30
31
|
return first;
|
|
31
32
|
}
|
|
32
|
-
const literals = leaves.
|
|
33
|
+
const literals = leaves.flatMap((leaf) => "literal" in leaf ? [leaf.literal] : []);
|
|
33
34
|
const types = literals.map((v) => literalType(v.value)).filter((v) => v !== void 0);
|
|
34
35
|
for (const literal of literals) checkLiteral(literal.value, types[0]);
|
|
35
36
|
return new LiteralCodec(types[0] ?? {
|
|
@@ -55,9 +56,17 @@ var LiteralCodec = class {
|
|
|
55
56
|
* Each kind selects its own value fields; only CASE's THEN and ELSE expressions contribute a result codec.
|
|
56
57
|
*/
|
|
57
58
|
function expressionLeaves(parsed) {
|
|
58
|
-
if (parsed instanceof require_Expr.BaseExpr) return [parsed];
|
|
59
|
+
if (parsed instanceof require_Expr.BaseExpr) return [{ codec: parsed }];
|
|
59
60
|
switch (parsed.kind) {
|
|
60
|
-
case "literal": return [parsed];
|
|
61
|
+
case "literal": return [{ literal: parsed }];
|
|
62
|
+
case "arrayAgg": {
|
|
63
|
+
const valueCodec = chooseExpressionCodec(parsed.value);
|
|
64
|
+
return [{ codec: {
|
|
65
|
+
outputType: require_TypeInfo.arrayOutputType(valueCodec.outputType),
|
|
66
|
+
encode: (value) => Array.isArray(value) ? value.map((element) => valueCodec.encode(element)) : value,
|
|
67
|
+
decode: (value) => Array.isArray(value) ? value.map((element) => valueCodec.decode(element)) : value
|
|
68
|
+
} }];
|
|
69
|
+
}
|
|
61
70
|
case "coalesce": return parsed.candidates.flatMap((candidate) => expressionLeaves(candidate));
|
|
62
71
|
case "nullIf": return [...expressionLeaves(parsed.value), ...expressionLeaves(parsed.equals)];
|
|
63
72
|
case "greatest":
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"codecs.cjs","names":["BaseExpr","assertNever","inspect"],"sources":["../../src/expressions/codecs.ts"],"sourcesContent":["import { inspect } from \"node:util\";\n\nimport { BaseExpr } from \"../Expr.ts\";\nimport type
|
|
1
|
+
{"version":3,"file":"codecs.cjs","names":["BaseExpr","arrayOutputType","assertNever","inspect"],"sources":["../../src/expressions/codecs.ts"],"sourcesContent":["import { inspect } from \"node:util\";\n\nimport { BaseExpr } from \"../Expr.ts\";\nimport { type TypeInfo, arrayOutputType } from \"../TypeInfo.ts\";\nimport { assertNever } from \"../utils.ts\";\nimport type { ParsedExpression, ParsedLiteralExpression, ResultCodec } from \"./types.ts\";\n\n/**\n * Chooses one decoder for all possible result values. Columns must agree on a compatible SQL type and domain;\n * literals use that column's encoder. This is conservative: PostgreSQL may accept other combinations,\n * but Joist cannot safely choose their decoder. I.e. Author.id and Book.id both store integers but use different tags.\n */\nexport function chooseExpressionCodec(parsed: ParsedExpression): ResultCodec {\n const leaves = expressionLeaves(parsed);\n const expressions = leaves.flatMap((leaf) => (\"codec\" in leaf ? [leaf.codec] : []));\n const first = expressions[0];\n if (first) {\n for (const other of expressions.slice(1)) {\n if (other === first) continue;\n const a = first.outputType;\n const b = other.outputType;\n if (!a || !b || !compatibleDbTypes(a.dbType, b.dbType) || a.domain !== b.domain || a.idMeta !== b.idMeta) {\n const mismatches =\n !a || !b\n ? [\"unknown codec\"]\n : [\n ...(!compatibleDbTypes(a.dbType, b.dbType) ? [\"SQL type\"] : []),\n ...(a.domain !== b.domain ? [\"domain\"] : []),\n ...(a.idMeta !== b.idMeta ? [\"ID target\"] : []),\n ];\n throw new Error(\n `Expression operands need matching SQL types and codecs: ${describeType(a)} vs ${describeType(b)}; mismatched ${mismatches.join(\", \")}`,\n );\n }\n }\n for (const leaf of leaves) if (\"literal\" in leaf) checkLiteral(leaf.literal.value, first.outputType);\n return first;\n }\n const literals = leaves.flatMap((leaf) => (\"literal\" in leaf ? [leaf.literal] : []));\n const types = literals.map((v) => literalType(v.value)).filter((v) => v !== undefined);\n for (const literal of literals) checkLiteral(literal.value, types[0]);\n return new LiteralCodec(types[0] ?? { dbType: \"text\", domain: String });\n}\n\n/** Gives literal-only expressions a SQL type so PostgreSQL does not return numbers as text. */\nclass LiteralCodec {\n constructor(readonly outputType: TypeInfo) {}\n\n encode(value: unknown): unknown {\n return value;\n }\n\n decode(value: unknown): unknown {\n return this.outputType.domain === BigInt ? BigInt(value as string) : value;\n }\n}\n\n/**\n * Finds parsed operands that share a codec, including NULLIF's comparison operand but not CASE conditions.\n * Each kind selects its own value fields; only CASE's THEN and ELSE expressions contribute a result codec.\n */\nfunction expressionLeaves(parsed: ParsedExpression): CodecLeaf[] {\n if (parsed instanceof BaseExpr) return [{ codec: parsed }];\n switch (parsed.kind) {\n case \"literal\":\n return [{ literal: parsed }];\n case \"arrayAgg\": {\n const valueCodec = chooseExpressionCodec(parsed.value);\n return [\n {\n codec: {\n outputType: arrayOutputType(valueCodec.outputType),\n encode: (value) => (Array.isArray(value) ? value.map((element) => valueCodec.encode(element)) : value),\n decode: (value) => (Array.isArray(value) ? value.map((element) => valueCodec.decode(element)) : value),\n },\n },\n ];\n }\n case \"coalesce\":\n return parsed.candidates.flatMap((candidate) => expressionLeaves(candidate));\n case \"nullIf\":\n return [...expressionLeaves(parsed.value), ...expressionLeaves(parsed.equals)];\n case \"greatest\":\n case \"least\":\n return parsed.values.flatMap((value) => expressionLeaves(value));\n case \"case\":\n return [\n ...parsed.whens.flatMap((entry) => expressionLeaves(entry.then)),\n ...(parsed.else ? expressionLeaves(parsed.else) : []),\n ];\n default:\n return assertNever(parsed);\n }\n}\n\n/** One result codec or one literal that will adopt a sibling codec. */\ntype CodecLeaf = { codec: ResultCodec } | { literal: ParsedLiteralExpression };\n\n/** Supplies predictable PostgreSQL types for standalone primitive literals. */\nfunction literalType(value: unknown): TypeInfo | undefined {\n if (value === null) return undefined;\n if (typeof value === \"string\") return { dbType: \"text\", domain: String, arrayElementSafe: true };\n if (typeof value === \"number\") return { dbType: \"float8\", domain: Number, arrayElementSafe: true };\n if (typeof value === \"boolean\") return { dbType: \"bool\", domain: Boolean, arrayElementSafe: true };\n if (typeof value === \"bigint\") return { dbType: \"int8\", domain: BigInt };\n if (value instanceof Date) return { dbType: \"timestamptz\", domain: Date };\n throw new Error(\"Object and array literals need an expression with a matching codec\");\n}\n\n/** Rejects primitive literals that disagree with the column; custom values are checked by their encoder. */\nfunction checkLiteral(value: unknown, type: TypeInfo | undefined): void {\n if (value === null || !type) return;\n const domain = type.domain;\n if (\n (domain === String && typeof value !== \"string\") ||\n (domain === Number && typeof value !== \"number\") ||\n (domain === Boolean && typeof value !== \"boolean\") ||\n (domain === BigInt && typeof value !== \"bigint\") ||\n (domain === Date && !(value instanceof Date))\n ) {\n throw new Error(\n `Expression values must have compatible types: expected ${describeType(type)}, got ${inspect(value)} (${typeof value})`,\n );\n }\n}\n\n/** Names the storage type, conversion domain, and entity tag involved in a codec mismatch. */\nfunction describeType(type: TypeInfo | undefined): string {\n if (!type) return \"unknown codec\";\n const domain = typeof type.domain === \"function\" ? type.domain.name : inspect(type.domain, { depth: 0 });\n return `${type.dbType} (domain ${domain}${type.idMeta ? `, ID target ${type.idMeta.type}` : \"\"})`;\n}\n\nconst compatibleStringDbTypes = new Set([\"text\", \"varchar\"]);\n\n/** Allows PostgreSQL scalar string types that resolve to a common string result. */\nfunction compatibleDbTypes(a: string, b: string): boolean {\n return a === b || (compatibleStringDbTypes.has(a) && compatibleStringDbTypes.has(b));\n}\n"],"mappings":"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"}
|
|
@@ -1,4 +1,5 @@
|
|
|
1
1
|
import { assertNever } from "../utils.js";
|
|
2
|
+
import { arrayOutputType } from "../TypeInfo.js";
|
|
2
3
|
import { BaseExpr } from "../Expr.js";
|
|
3
4
|
import { inspect } from "node:util";
|
|
4
5
|
//#region src/expressions/codecs.ts
|
|
@@ -9,7 +10,7 @@ import { inspect } from "node:util";
|
|
|
9
10
|
*/
|
|
10
11
|
function chooseExpressionCodec(parsed) {
|
|
11
12
|
const leaves = expressionLeaves(parsed);
|
|
12
|
-
const expressions = leaves.
|
|
13
|
+
const expressions = leaves.flatMap((leaf) => "codec" in leaf ? [leaf.codec] : []);
|
|
13
14
|
const first = expressions[0];
|
|
14
15
|
if (first) {
|
|
15
16
|
for (const other of expressions.slice(1)) {
|
|
@@ -25,10 +26,10 @@ function chooseExpressionCodec(parsed) {
|
|
|
25
26
|
throw new Error(`Expression operands need matching SQL types and codecs: ${describeType(a)} vs ${describeType(b)}; mismatched ${mismatches.join(", ")}`);
|
|
26
27
|
}
|
|
27
28
|
}
|
|
28
|
-
for (const leaf of leaves) if (
|
|
29
|
+
for (const leaf of leaves) if ("literal" in leaf) checkLiteral(leaf.literal.value, first.outputType);
|
|
29
30
|
return first;
|
|
30
31
|
}
|
|
31
|
-
const literals = leaves.
|
|
32
|
+
const literals = leaves.flatMap((leaf) => "literal" in leaf ? [leaf.literal] : []);
|
|
32
33
|
const types = literals.map((v) => literalType(v.value)).filter((v) => v !== void 0);
|
|
33
34
|
for (const literal of literals) checkLiteral(literal.value, types[0]);
|
|
34
35
|
return new LiteralCodec(types[0] ?? {
|
|
@@ -54,9 +55,17 @@ var LiteralCodec = class {
|
|
|
54
55
|
* Each kind selects its own value fields; only CASE's THEN and ELSE expressions contribute a result codec.
|
|
55
56
|
*/
|
|
56
57
|
function expressionLeaves(parsed) {
|
|
57
|
-
if (parsed instanceof BaseExpr) return [parsed];
|
|
58
|
+
if (parsed instanceof BaseExpr) return [{ codec: parsed }];
|
|
58
59
|
switch (parsed.kind) {
|
|
59
|
-
case "literal": return [parsed];
|
|
60
|
+
case "literal": return [{ literal: parsed }];
|
|
61
|
+
case "arrayAgg": {
|
|
62
|
+
const valueCodec = chooseExpressionCodec(parsed.value);
|
|
63
|
+
return [{ codec: {
|
|
64
|
+
outputType: arrayOutputType(valueCodec.outputType),
|
|
65
|
+
encode: (value) => Array.isArray(value) ? value.map((element) => valueCodec.encode(element)) : value,
|
|
66
|
+
decode: (value) => Array.isArray(value) ? value.map((element) => valueCodec.decode(element)) : value
|
|
67
|
+
} }];
|
|
68
|
+
}
|
|
60
69
|
case "coalesce": return parsed.candidates.flatMap((candidate) => expressionLeaves(candidate));
|
|
61
70
|
case "nullIf": return [...expressionLeaves(parsed.value), ...expressionLeaves(parsed.equals)];
|
|
62
71
|
case "greatest":
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"codecs.js","names":[],"sources":["../../src/expressions/codecs.ts"],"sourcesContent":["import { inspect } from \"node:util\";\n\nimport { BaseExpr } from \"../Expr.ts\";\nimport type
|
|
1
|
+
{"version":3,"file":"codecs.js","names":[],"sources":["../../src/expressions/codecs.ts"],"sourcesContent":["import { inspect } from \"node:util\";\n\nimport { BaseExpr } from \"../Expr.ts\";\nimport { type TypeInfo, arrayOutputType } from \"../TypeInfo.ts\";\nimport { assertNever } from \"../utils.ts\";\nimport type { ParsedExpression, ParsedLiteralExpression, ResultCodec } from \"./types.ts\";\n\n/**\n * Chooses one decoder for all possible result values. Columns must agree on a compatible SQL type and domain;\n * literals use that column's encoder. This is conservative: PostgreSQL may accept other combinations,\n * but Joist cannot safely choose their decoder. I.e. Author.id and Book.id both store integers but use different tags.\n */\nexport function chooseExpressionCodec(parsed: ParsedExpression): ResultCodec {\n const leaves = expressionLeaves(parsed);\n const expressions = leaves.flatMap((leaf) => (\"codec\" in leaf ? [leaf.codec] : []));\n const first = expressions[0];\n if (first) {\n for (const other of expressions.slice(1)) {\n if (other === first) continue;\n const a = first.outputType;\n const b = other.outputType;\n if (!a || !b || !compatibleDbTypes(a.dbType, b.dbType) || a.domain !== b.domain || a.idMeta !== b.idMeta) {\n const mismatches =\n !a || !b\n ? [\"unknown codec\"]\n : [\n ...(!compatibleDbTypes(a.dbType, b.dbType) ? [\"SQL type\"] : []),\n ...(a.domain !== b.domain ? [\"domain\"] : []),\n ...(a.idMeta !== b.idMeta ? [\"ID target\"] : []),\n ];\n throw new Error(\n `Expression operands need matching SQL types and codecs: ${describeType(a)} vs ${describeType(b)}; mismatched ${mismatches.join(\", \")}`,\n );\n }\n }\n for (const leaf of leaves) if (\"literal\" in leaf) checkLiteral(leaf.literal.value, first.outputType);\n return first;\n }\n const literals = leaves.flatMap((leaf) => (\"literal\" in leaf ? [leaf.literal] : []));\n const types = literals.map((v) => literalType(v.value)).filter((v) => v !== undefined);\n for (const literal of literals) checkLiteral(literal.value, types[0]);\n return new LiteralCodec(types[0] ?? { dbType: \"text\", domain: String });\n}\n\n/** Gives literal-only expressions a SQL type so PostgreSQL does not return numbers as text. */\nclass LiteralCodec {\n constructor(readonly outputType: TypeInfo) {}\n\n encode(value: unknown): unknown {\n return value;\n }\n\n decode(value: unknown): unknown {\n return this.outputType.domain === BigInt ? BigInt(value as string) : value;\n }\n}\n\n/**\n * Finds parsed operands that share a codec, including NULLIF's comparison operand but not CASE conditions.\n * Each kind selects its own value fields; only CASE's THEN and ELSE expressions contribute a result codec.\n */\nfunction expressionLeaves(parsed: ParsedExpression): CodecLeaf[] {\n if (parsed instanceof BaseExpr) return [{ codec: parsed }];\n switch (parsed.kind) {\n case \"literal\":\n return [{ literal: parsed }];\n case \"arrayAgg\": {\n const valueCodec = chooseExpressionCodec(parsed.value);\n return [\n {\n codec: {\n outputType: arrayOutputType(valueCodec.outputType),\n encode: (value) => (Array.isArray(value) ? value.map((element) => valueCodec.encode(element)) : value),\n decode: (value) => (Array.isArray(value) ? value.map((element) => valueCodec.decode(element)) : value),\n },\n },\n ];\n }\n case \"coalesce\":\n return parsed.candidates.flatMap((candidate) => expressionLeaves(candidate));\n case \"nullIf\":\n return [...expressionLeaves(parsed.value), ...expressionLeaves(parsed.equals)];\n case \"greatest\":\n case \"least\":\n return parsed.values.flatMap((value) => expressionLeaves(value));\n case \"case\":\n return [\n ...parsed.whens.flatMap((entry) => expressionLeaves(entry.then)),\n ...(parsed.else ? expressionLeaves(parsed.else) : []),\n ];\n default:\n return assertNever(parsed);\n }\n}\n\n/** One result codec or one literal that will adopt a sibling codec. */\ntype CodecLeaf = { codec: ResultCodec } | { literal: ParsedLiteralExpression };\n\n/** Supplies predictable PostgreSQL types for standalone primitive literals. */\nfunction literalType(value: unknown): TypeInfo | undefined {\n if (value === null) return undefined;\n if (typeof value === \"string\") return { dbType: \"text\", domain: String, arrayElementSafe: true };\n if (typeof value === \"number\") return { dbType: \"float8\", domain: Number, arrayElementSafe: true };\n if (typeof value === \"boolean\") return { dbType: \"bool\", domain: Boolean, arrayElementSafe: true };\n if (typeof value === \"bigint\") return { dbType: \"int8\", domain: BigInt };\n if (value instanceof Date) return { dbType: \"timestamptz\", domain: Date };\n throw new Error(\"Object and array literals need an expression with a matching codec\");\n}\n\n/** Rejects primitive literals that disagree with the column; custom values are checked by their encoder. */\nfunction checkLiteral(value: unknown, type: TypeInfo | undefined): void {\n if (value === null || !type) return;\n const domain = type.domain;\n if (\n (domain === String && typeof value !== \"string\") ||\n (domain === Number && typeof value !== \"number\") ||\n (domain === Boolean && typeof value !== \"boolean\") ||\n (domain === BigInt && typeof value !== \"bigint\") ||\n (domain === Date && !(value instanceof Date))\n ) {\n throw new Error(\n `Expression values must have compatible types: expected ${describeType(type)}, got ${inspect(value)} (${typeof value})`,\n );\n }\n}\n\n/** Names the storage type, conversion domain, and entity tag involved in a codec mismatch. */\nfunction describeType(type: TypeInfo | undefined): string {\n if (!type) return \"unknown codec\";\n const domain = typeof type.domain === \"function\" ? type.domain.name : inspect(type.domain, { depth: 0 });\n return `${type.dbType} (domain ${domain}${type.idMeta ? `, ID target ${type.idMeta.type}` : \"\"})`;\n}\n\nconst compatibleStringDbTypes = new Set([\"text\", \"varchar\"]);\n\n/** Allows PostgreSQL scalar string types that resolve to a common string result. */\nfunction compatibleDbTypes(a: string, b: string): boolean {\n return a === b || (compatibleStringDbTypes.has(a) && compatibleStringDbTypes.has(b));\n}\n"],"mappings":"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"}
|
|
@@ -1,12 +1,13 @@
|
|
|
1
1
|
Object.defineProperty(exports, Symbol.toStringTag, { value: "Module" });
|
|
2
2
|
const require_utils = require("../utils.cjs");
|
|
3
3
|
const require_Expr = require("../Expr.cjs");
|
|
4
|
+
const require_expressions_case = require("./case.cjs");
|
|
4
5
|
const require_expressions_coalesce = require("./coalesce.cjs");
|
|
5
6
|
const require_expressions_greatest = require("./greatest.cjs");
|
|
6
7
|
const require_expressions_least = require("./least.cjs");
|
|
7
8
|
const require_expressions_nullIf = require("./nullIf.cjs");
|
|
8
9
|
const require_expressions_parseExpression = require("./parseExpression.cjs");
|
|
9
|
-
const
|
|
10
|
+
const require_expressions_arrayAgg = require("./arrayAgg.cjs");
|
|
10
11
|
const require_expressions_codecs = require("./codecs.cjs");
|
|
11
12
|
//#region src/expressions/expression.ts
|
|
12
13
|
/** Builds a reusable SQL value expression, binding literal values as parameters. */
|
|
@@ -50,6 +51,7 @@ function expressionNullable(parsed) {
|
|
|
50
51
|
if (parsed instanceof require_Expr.BaseExpr) return parsed.sqlSource ? void 0 : parsed.sqlNullable;
|
|
51
52
|
switch (parsed.kind) {
|
|
52
53
|
case "literal": return parsed.value === null;
|
|
54
|
+
case "arrayAgg": return true;
|
|
53
55
|
case "nullIf": return require_expressions_nullIf.nullIfNullable();
|
|
54
56
|
case "coalesce": return require_expressions_coalesce.coalesceNullable(parsed);
|
|
55
57
|
case "greatest": return require_expressions_greatest.greatestNullable(parsed);
|
|
@@ -73,6 +75,7 @@ function expressionToSql(parsed, ctx, codec) {
|
|
|
73
75
|
refs: []
|
|
74
76
|
};
|
|
75
77
|
}
|
|
78
|
+
case "arrayAgg": return require_expressions_arrayAgg.arrayAggToSql(parsed, ctx, require_expressions_codecs.chooseExpressionCodec(parsed.value));
|
|
76
79
|
case "coalesce": return require_expressions_coalesce.coalesceToSql(parsed, ctx, codec);
|
|
77
80
|
case "nullIf": return require_expressions_nullIf.nullIfToSql(parsed, ctx, codec);
|
|
78
81
|
case "greatest": return require_expressions_greatest.greatestToSql(parsed, ctx, codec);
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"expression.cjs","names":["parseExpressionInput","BaseExpr","chooseExpressionCodec","nullIfNullable","coalesceNullable","greatestNullable","leastNullable","caseNullable","assertNever","coalesceToSql","nullIfToSql","greatestToSql","leastToSql","caseToSql","joinFragments"],"sources":["../../src/expressions/expression.ts"],"sourcesContent":["import { BaseExpr, type ExprContext, type SqlFragment, joinFragments } from \"../Expr.ts\";\nimport type { TypeInfo } from \"../TypeInfo.ts\";\nimport { assertNever } from \"../utils.ts\";\nimport { caseNullable, caseToSql } from \"./case.ts\";\nimport { coalesceNullable, coalesceToSql } from \"./coalesce.ts\";\nimport { chooseExpressionCodec } from \"./codecs.ts\";\nimport { greatestNullable, greatestToSql } from \"./greatest.ts\";\nimport { leastNullable, leastToSql } from \"./least.ts\";\nimport { nullIfNullable, nullIfToSql } from \"./nullIf.ts\";\nimport { parseExpressionInput } from \"./parseExpression.ts\";\nimport type { CheckInput, ExprFromInput, ExprInput, ParsedExpression, ResultCodec } from \"./types.ts\";\n\nexport type { CaseElse, CaseWhen } from \"./case.ts\";\nexport type { CheckInput, ExprFromInput, ExprInput, ExpressionSources, ExpressionValue } from \"./types.ts\";\n\n/** Builds a reusable SQL value expression, binding literal values as parameters. */\nexport function expr<const I extends ExprInput>(input: I & CheckInput<NoInfer<I>>): ExprFromInput<I> {\n return buildExpr(input) as unknown as ExprFromInput<I>;\n}\n\n/** Parses explicit and inline inputs into a ParsedExpression, then wraps it in an Expr with a result codec. */\nexport function buildExpr(input: unknown): BaseExpr {\n return new ObjectExpr(parseExpressionInput(input));\n}\n\n/** Exposes a ParsedExpression through the Expr methods, SQL rendering, and shared result codec. */\nclass ObjectExpr extends BaseExpr {\n private readonly codec: ResultCodec;\n\n constructor(private readonly parsed: ParsedExpression) {\n super();\n this.codec = chooseExpressionCodec(parsed);\n }\n\n get outputType(): TypeInfo | undefined {\n return this.codec.outputType;\n }\n\n get sqlNullable(): boolean | undefined {\n return expressionNullable(this.parsed);\n }\n\n toSql(ctx: ExprContext): SqlFragment {\n return expressionToSql(this.parsed, ctx, this.codec);\n }\n\n decode(value: unknown): unknown {\n return value == null ? value : this.codec.decode(value);\n }\n\n encode(value: unknown): unknown {\n return value == null ? value : this.codec.encode(value);\n }\n}\n\n/**\n * Reports nullability from the fields of each parsed expression kind.\n * A direct column can become null through a LEFT join, so only independent values prove NOT NULL here.\n */\nexport function expressionNullable(parsed: ParsedExpression): boolean | undefined {\n if (parsed instanceof BaseExpr) return parsed.sqlSource ? undefined : parsed.sqlNullable;\n switch (parsed.kind) {\n case \"literal\":\n return parsed.value === null;\n case \"nullIf\":\n return nullIfNullable();\n case \"coalesce\":\n return coalesceNullable(parsed);\n case \"greatest\":\n return greatestNullable(parsed);\n case \"least\":\n return leastNullable(parsed);\n case \"case\":\n return caseNullable(parsed);\n default:\n return assertNever(parsed);\n }\n}\n\n/**\n * Renders a ParsedExpression in SQL binding order, resolving CASE conditions and collecting references for join pruning.\n * An omitted CASE condition removes its value as well. I.e. an unused Book.title branch must not keep the Book join.\n */\nexport function expressionToSql(parsed: ParsedExpression, ctx: ExprContext, codec: ResultCodec): SqlFragment {\n if (parsed instanceof BaseExpr) return parsed.toSql(ctx);\n switch (parsed.kind) {\n case \"literal\": {\n const dbType = codec.outputType?.dbType;\n return {\n sql: dbType ? `?::${dbType}` : \"?\",\n bindings: [parsed.value === null ? null : codec.encode(parsed.value)],\n refs: [],\n };\n }\n case \"coalesce\":\n return coalesceToSql(parsed, ctx, codec);\n case \"nullIf\":\n return nullIfToSql(parsed, ctx, codec);\n case \"greatest\":\n return greatestToSql(parsed, ctx, codec);\n case \"least\":\n return leastToSql(parsed, ctx, codec);\n case \"case\":\n return caseToSql(parsed, ctx, codec);\n default:\n return assertNever(parsed);\n }\n}\n\n/** Renders a function call after its parsed kind has supplied the arguments in SQL order. */\nexport function functionToSql(\n name: string,\n args: readonly ParsedExpression[],\n ctx: ExprContext,\n codec: ResultCodec,\n): SqlFragment {\n const parts = joinFragments(\n args.map((arg) => expressionToSql(arg, ctx, codec)),\n \", \",\n );\n return { ...parts, sql: `${name}(${parts.sql})` };\n}\n"],"mappings":"
|
|
1
|
+
{"version":3,"file":"expression.cjs","names":["parseExpressionInput","BaseExpr","chooseExpressionCodec","nullIfNullable","coalesceNullable","greatestNullable","leastNullable","caseNullable","assertNever","arrayAggToSql","coalesceToSql","nullIfToSql","greatestToSql","leastToSql","caseToSql","joinFragments"],"sources":["../../src/expressions/expression.ts"],"sourcesContent":["import { BaseExpr, type ExprContext, type SqlFragment, joinFragments } from \"../Expr.ts\";\nimport type { TypeInfo } from \"../TypeInfo.ts\";\nimport { assertNever } from \"../utils.ts\";\nimport { arrayAggToSql } from \"./arrayAgg.ts\";\nimport { caseNullable, caseToSql } from \"./case.ts\";\nimport { coalesceNullable, coalesceToSql } from \"./coalesce.ts\";\nimport { chooseExpressionCodec } from \"./codecs.ts\";\nimport { greatestNullable, greatestToSql } from \"./greatest.ts\";\nimport { leastNullable, leastToSql } from \"./least.ts\";\nimport { nullIfNullable, nullIfToSql } from \"./nullIf.ts\";\nimport { parseExpressionInput } from \"./parseExpression.ts\";\nimport type { CheckInput, ExprFromInput, ExprInput, ParsedExpression, ResultCodec } from \"./types.ts\";\n\nexport type { CaseElse, CaseWhen } from \"./case.ts\";\nexport type { CheckInput, ExprFromInput, ExprInput, ExpressionSources, ExpressionValue } from \"./types.ts\";\n\n/** Builds a reusable SQL value expression, binding literal values as parameters. */\nexport function expr<const I extends ExprInput>(input: I & CheckInput<NoInfer<I>>): ExprFromInput<I> {\n return buildExpr(input) as unknown as ExprFromInput<I>;\n}\n\n/** Parses explicit and inline inputs into a ParsedExpression, then wraps it in an Expr with a result codec. */\nexport function buildExpr(input: unknown): BaseExpr {\n return new ObjectExpr(parseExpressionInput(input));\n}\n\n/** Exposes a ParsedExpression through the Expr methods, SQL rendering, and shared result codec. */\nclass ObjectExpr extends BaseExpr {\n private readonly codec: ResultCodec;\n\n constructor(private readonly parsed: ParsedExpression) {\n super();\n this.codec = chooseExpressionCodec(parsed);\n }\n\n get outputType(): TypeInfo | undefined {\n return this.codec.outputType;\n }\n\n get sqlNullable(): boolean | undefined {\n return expressionNullable(this.parsed);\n }\n\n toSql(ctx: ExprContext): SqlFragment {\n return expressionToSql(this.parsed, ctx, this.codec);\n }\n\n decode(value: unknown): unknown {\n return value == null ? value : this.codec.decode(value);\n }\n\n encode(value: unknown): unknown {\n return value == null ? value : this.codec.encode(value);\n }\n}\n\n/**\n * Reports nullability from the fields of each parsed expression kind.\n * A direct column can become null through a LEFT join, so only independent values prove NOT NULL here.\n */\nexport function expressionNullable(parsed: ParsedExpression): boolean | undefined {\n if (parsed instanceof BaseExpr) return parsed.sqlSource ? undefined : parsed.sqlNullable;\n switch (parsed.kind) {\n case \"literal\":\n return parsed.value === null;\n case \"arrayAgg\":\n return true;\n case \"nullIf\":\n return nullIfNullable();\n case \"coalesce\":\n return coalesceNullable(parsed);\n case \"greatest\":\n return greatestNullable(parsed);\n case \"least\":\n return leastNullable(parsed);\n case \"case\":\n return caseNullable(parsed);\n default:\n return assertNever(parsed);\n }\n}\n\n/**\n * Renders a ParsedExpression in SQL binding order, resolving CASE conditions and collecting references for join pruning.\n * An omitted CASE condition removes its value as well. I.e. an unused Book.title branch must not keep the Book join.\n */\nexport function expressionToSql(parsed: ParsedExpression, ctx: ExprContext, codec: ResultCodec): SqlFragment {\n if (parsed instanceof BaseExpr) return parsed.toSql(ctx);\n switch (parsed.kind) {\n case \"literal\": {\n const dbType = codec.outputType?.dbType;\n return {\n sql: dbType ? `?::${dbType}` : \"?\",\n bindings: [parsed.value === null ? null : codec.encode(parsed.value)],\n refs: [],\n };\n }\n case \"arrayAgg\":\n return arrayAggToSql(parsed, ctx, chooseExpressionCodec(parsed.value));\n case \"coalesce\":\n return coalesceToSql(parsed, ctx, codec);\n case \"nullIf\":\n return nullIfToSql(parsed, ctx, codec);\n case \"greatest\":\n return greatestToSql(parsed, ctx, codec);\n case \"least\":\n return leastToSql(parsed, ctx, codec);\n case \"case\":\n return caseToSql(parsed, ctx, codec);\n default:\n return assertNever(parsed);\n }\n}\n\n/** Renders a function call after its parsed kind has supplied the arguments in SQL order. */\nexport function functionToSql(\n name: string,\n args: readonly ParsedExpression[],\n ctx: ExprContext,\n codec: ResultCodec,\n): SqlFragment {\n const parts = joinFragments(\n args.map((arg) => expressionToSql(arg, ctx, codec)),\n \", \",\n );\n return { ...parts, sql: `${name}(${parts.sql})` };\n}\n"],"mappings":";;;;;;;;;;;;;AAiBA,SAAgB,KAAgC,OAAqD;CACnG,OAAO,UAAU,KAAK;AACxB;;AAGA,SAAgB,UAAU,OAA0B;CAClD,OAAO,IAAI,WAAWA,oCAAAA,qBAAqB,KAAK,CAAC;AACnD;;AAGA,IAAM,aAAN,cAAyBC,aAAAA,SAAS;CAGH;CAF7B;CAEA,YAAY,QAA2C;EACrD,MAAM;EADqB,KAAA,SAAA;EAE3B,KAAK,QAAQC,2BAAAA,sBAAsB,MAAM;CAC3C;CAEA,IAAI,aAAmC;EACrC,OAAO,KAAK,MAAM;CACpB;CAEA,IAAI,cAAmC;EACrC,OAAO,mBAAmB,KAAK,MAAM;CACvC;CAEA,MAAM,KAA+B;EACnC,OAAO,gBAAgB,KAAK,QAAQ,KAAK,KAAK,KAAK;CACrD;CAEA,OAAO,OAAyB;EAC9B,OAAO,SAAS,OAAO,QAAQ,KAAK,MAAM,OAAO,KAAK;CACxD;CAEA,OAAO,OAAyB;EAC9B,OAAO,SAAS,OAAO,QAAQ,KAAK,MAAM,OAAO,KAAK;CACxD;AACF;;;;;AAMA,SAAgB,mBAAmB,QAA+C;CAChF,IAAI,kBAAkBD,aAAAA,UAAU,OAAO,OAAO,YAAY,KAAA,IAAY,OAAO;CAC7E,QAAQ,OAAO,MAAf;EACE,KAAK,WACH,OAAO,OAAO,UAAU;EAC1B,KAAK,YACH,OAAO;EACT,KAAK,UACH,OAAOE,2BAAAA,eAAe;EACxB,KAAK,YACH,OAAOC,6BAAAA,iBAAiB,MAAM;EAChC,KAAK,YACH,OAAOC,6BAAAA,iBAAiB,MAAM;EAChC,KAAK,SACH,OAAOC,0BAAAA,cAAc,MAAM;EAC7B,KAAK,QACH,OAAOC,yBAAAA,aAAa,MAAM;EAC5B,SACE,OAAOC,cAAAA,YAAY,MAAM;CAC7B;AACF;;;;;AAMA,SAAgB,gBAAgB,QAA0B,KAAkB,OAAiC;CAC3G,IAAI,kBAAkBP,aAAAA,UAAU,OAAO,OAAO,MAAM,GAAG;CACvD,QAAQ,OAAO,MAAf;EACE,KAAK,WAAW;GACd,MAAM,SAAS,MAAM,YAAY;GACjC,OAAO;IACL,KAAK,SAAS,MAAM,WAAW;IAC/B,UAAU,CAAC,OAAO,UAAU,OAAO,OAAO,MAAM,OAAO,OAAO,KAAK,CAAC;IACpE,MAAM,CAAC;GACT;EACF;EACA,KAAK,YACH,OAAOQ,6BAAAA,cAAc,QAAQ,KAAKP,2BAAAA,sBAAsB,OAAO,KAAK,CAAC;EACvE,KAAK,YACH,OAAOQ,6BAAAA,cAAc,QAAQ,KAAK,KAAK;EACzC,KAAK,UACH,OAAOC,2BAAAA,YAAY,QAAQ,KAAK,KAAK;EACvC,KAAK,YACH,OAAOC,6BAAAA,cAAc,QAAQ,KAAK,KAAK;EACzC,KAAK,SACH,OAAOC,0BAAAA,WAAW,QAAQ,KAAK,KAAK;EACtC,KAAK,QACH,OAAOC,yBAAAA,UAAU,QAAQ,KAAK,KAAK;EACrC,SACE,OAAON,cAAAA,YAAY,MAAM;CAC7B;AACF;;AAGA,SAAgB,cACd,MACA,MACA,KACA,OACa;CACb,MAAM,QAAQO,aAAAA,cACZ,KAAK,KAAK,QAAQ,gBAAgB,KAAK,KAAK,KAAK,CAAC,GAClD,IACF;CACA,OAAO;EAAE,GAAG;EAAO,KAAK,GAAG,KAAK,GAAG,MAAM,IAAI;CAAG;AAClD"}
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"expression.d.cts","names":[],"sources":["../../src/expressions/expression.ts"],"mappings":";;;;;
|
|
1
|
+
{"version":3,"file":"expression.d.cts","names":[],"sources":["../../src/expressions/expression.ts"],"mappings":";;;;;iBAiBgB,WAAW,UAAU,WAAW,OAAO,IAAI,WAAW,QAAQ,MAAM,cAAc;;iBAKlF,UAAU,iBAAiB;;;;;iBAsC3B,mBAAmB,QAAQ;;;;;iBA0B3B,gBAAgB,QAAQ,kBAAkB,KAAK,aAAa,OAAO,cAAc;;iBA6BjF,cACd,cACA,eAAe,oBACf,KAAK,aACL,OAAO,cACN"}
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"expression.d.mts","names":[],"sources":["../../src/expressions/expression.ts"],"mappings":";;;;;
|
|
1
|
+
{"version":3,"file":"expression.d.mts","names":[],"sources":["../../src/expressions/expression.ts"],"mappings":";;;;;iBAiBgB,WAAW,UAAU,WAAW,OAAO,IAAI,WAAW,QAAQ,MAAM,cAAc;;iBAKlF,UAAU,iBAAiB;;;;;iBAsC3B,mBAAmB,QAAQ;;;;;iBA0B3B,gBAAgB,QAAQ,kBAAkB,KAAK,aAAa,OAAO,cAAc;;iBA6BjF,cACd,cACA,eAAe,oBACf,KAAK,aACL,OAAO,cACN"}
|
|
@@ -1,11 +1,12 @@
|
|
|
1
1
|
import { assertNever } from "../utils.js";
|
|
2
2
|
import { BaseExpr, joinFragments } from "../Expr.js";
|
|
3
|
+
import { caseNullable, caseToSql } from "./case.js";
|
|
3
4
|
import { coalesceNullable, coalesceToSql } from "./coalesce.js";
|
|
4
5
|
import { greatestNullable, greatestToSql } from "./greatest.js";
|
|
5
6
|
import { leastNullable, leastToSql } from "./least.js";
|
|
6
7
|
import { nullIfNullable, nullIfToSql } from "./nullIf.js";
|
|
7
8
|
import { parseExpressionInput } from "./parseExpression.js";
|
|
8
|
-
import {
|
|
9
|
+
import { arrayAggToSql } from "./arrayAgg.js";
|
|
9
10
|
import { chooseExpressionCodec } from "./codecs.js";
|
|
10
11
|
//#region src/expressions/expression.ts
|
|
11
12
|
/** Builds a reusable SQL value expression, binding literal values as parameters. */
|
|
@@ -49,6 +50,7 @@ function expressionNullable(parsed) {
|
|
|
49
50
|
if (parsed instanceof BaseExpr) return parsed.sqlSource ? void 0 : parsed.sqlNullable;
|
|
50
51
|
switch (parsed.kind) {
|
|
51
52
|
case "literal": return parsed.value === null;
|
|
53
|
+
case "arrayAgg": return true;
|
|
52
54
|
case "nullIf": return nullIfNullable();
|
|
53
55
|
case "coalesce": return coalesceNullable(parsed);
|
|
54
56
|
case "greatest": return greatestNullable(parsed);
|
|
@@ -72,6 +74,7 @@ function expressionToSql(parsed, ctx, codec) {
|
|
|
72
74
|
refs: []
|
|
73
75
|
};
|
|
74
76
|
}
|
|
77
|
+
case "arrayAgg": return arrayAggToSql(parsed, ctx, chooseExpressionCodec(parsed.value));
|
|
75
78
|
case "coalesce": return coalesceToSql(parsed, ctx, codec);
|
|
76
79
|
case "nullIf": return nullIfToSql(parsed, ctx, codec);
|
|
77
80
|
case "greatest": return greatestToSql(parsed, ctx, codec);
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"expression.js","names":[],"sources":["../../src/expressions/expression.ts"],"sourcesContent":["import { BaseExpr, type ExprContext, type SqlFragment, joinFragments } from \"../Expr.ts\";\nimport type { TypeInfo } from \"../TypeInfo.ts\";\nimport { assertNever } from \"../utils.ts\";\nimport { caseNullable, caseToSql } from \"./case.ts\";\nimport { coalesceNullable, coalesceToSql } from \"./coalesce.ts\";\nimport { chooseExpressionCodec } from \"./codecs.ts\";\nimport { greatestNullable, greatestToSql } from \"./greatest.ts\";\nimport { leastNullable, leastToSql } from \"./least.ts\";\nimport { nullIfNullable, nullIfToSql } from \"./nullIf.ts\";\nimport { parseExpressionInput } from \"./parseExpression.ts\";\nimport type { CheckInput, ExprFromInput, ExprInput, ParsedExpression, ResultCodec } from \"./types.ts\";\n\nexport type { CaseElse, CaseWhen } from \"./case.ts\";\nexport type { CheckInput, ExprFromInput, ExprInput, ExpressionSources, ExpressionValue } from \"./types.ts\";\n\n/** Builds a reusable SQL value expression, binding literal values as parameters. */\nexport function expr<const I extends ExprInput>(input: I & CheckInput<NoInfer<I>>): ExprFromInput<I> {\n return buildExpr(input) as unknown as ExprFromInput<I>;\n}\n\n/** Parses explicit and inline inputs into a ParsedExpression, then wraps it in an Expr with a result codec. */\nexport function buildExpr(input: unknown): BaseExpr {\n return new ObjectExpr(parseExpressionInput(input));\n}\n\n/** Exposes a ParsedExpression through the Expr methods, SQL rendering, and shared result codec. */\nclass ObjectExpr extends BaseExpr {\n private readonly codec: ResultCodec;\n\n constructor(private readonly parsed: ParsedExpression) {\n super();\n this.codec = chooseExpressionCodec(parsed);\n }\n\n get outputType(): TypeInfo | undefined {\n return this.codec.outputType;\n }\n\n get sqlNullable(): boolean | undefined {\n return expressionNullable(this.parsed);\n }\n\n toSql(ctx: ExprContext): SqlFragment {\n return expressionToSql(this.parsed, ctx, this.codec);\n }\n\n decode(value: unknown): unknown {\n return value == null ? value : this.codec.decode(value);\n }\n\n encode(value: unknown): unknown {\n return value == null ? value : this.codec.encode(value);\n }\n}\n\n/**\n * Reports nullability from the fields of each parsed expression kind.\n * A direct column can become null through a LEFT join, so only independent values prove NOT NULL here.\n */\nexport function expressionNullable(parsed: ParsedExpression): boolean | undefined {\n if (parsed instanceof BaseExpr) return parsed.sqlSource ? undefined : parsed.sqlNullable;\n switch (parsed.kind) {\n case \"literal\":\n return parsed.value === null;\n case \"nullIf\":\n return nullIfNullable();\n case \"coalesce\":\n return coalesceNullable(parsed);\n case \"greatest\":\n return greatestNullable(parsed);\n case \"least\":\n return leastNullable(parsed);\n case \"case\":\n return caseNullable(parsed);\n default:\n return assertNever(parsed);\n }\n}\n\n/**\n * Renders a ParsedExpression in SQL binding order, resolving CASE conditions and collecting references for join pruning.\n * An omitted CASE condition removes its value as well. I.e. an unused Book.title branch must not keep the Book join.\n */\nexport function expressionToSql(parsed: ParsedExpression, ctx: ExprContext, codec: ResultCodec): SqlFragment {\n if (parsed instanceof BaseExpr) return parsed.toSql(ctx);\n switch (parsed.kind) {\n case \"literal\": {\n const dbType = codec.outputType?.dbType;\n return {\n sql: dbType ? `?::${dbType}` : \"?\",\n bindings: [parsed.value === null ? null : codec.encode(parsed.value)],\n refs: [],\n };\n }\n case \"coalesce\":\n return coalesceToSql(parsed, ctx, codec);\n case \"nullIf\":\n return nullIfToSql(parsed, ctx, codec);\n case \"greatest\":\n return greatestToSql(parsed, ctx, codec);\n case \"least\":\n return leastToSql(parsed, ctx, codec);\n case \"case\":\n return caseToSql(parsed, ctx, codec);\n default:\n return assertNever(parsed);\n }\n}\n\n/** Renders a function call after its parsed kind has supplied the arguments in SQL order. */\nexport function functionToSql(\n name: string,\n args: readonly ParsedExpression[],\n ctx: ExprContext,\n codec: ResultCodec,\n): SqlFragment {\n const parts = joinFragments(\n args.map((arg) => expressionToSql(arg, ctx, codec)),\n \", \",\n );\n return { ...parts, sql: `${name}(${parts.sql})` };\n}\n"],"mappings":"
|
|
1
|
+
{"version":3,"file":"expression.js","names":[],"sources":["../../src/expressions/expression.ts"],"sourcesContent":["import { BaseExpr, type ExprContext, type SqlFragment, joinFragments } from \"../Expr.ts\";\nimport type { TypeInfo } from \"../TypeInfo.ts\";\nimport { assertNever } from \"../utils.ts\";\nimport { arrayAggToSql } from \"./arrayAgg.ts\";\nimport { caseNullable, caseToSql } from \"./case.ts\";\nimport { coalesceNullable, coalesceToSql } from \"./coalesce.ts\";\nimport { chooseExpressionCodec } from \"./codecs.ts\";\nimport { greatestNullable, greatestToSql } from \"./greatest.ts\";\nimport { leastNullable, leastToSql } from \"./least.ts\";\nimport { nullIfNullable, nullIfToSql } from \"./nullIf.ts\";\nimport { parseExpressionInput } from \"./parseExpression.ts\";\nimport type { CheckInput, ExprFromInput, ExprInput, ParsedExpression, ResultCodec } from \"./types.ts\";\n\nexport type { CaseElse, CaseWhen } from \"./case.ts\";\nexport type { CheckInput, ExprFromInput, ExprInput, ExpressionSources, ExpressionValue } from \"./types.ts\";\n\n/** Builds a reusable SQL value expression, binding literal values as parameters. */\nexport function expr<const I extends ExprInput>(input: I & CheckInput<NoInfer<I>>): ExprFromInput<I> {\n return buildExpr(input) as unknown as ExprFromInput<I>;\n}\n\n/** Parses explicit and inline inputs into a ParsedExpression, then wraps it in an Expr with a result codec. */\nexport function buildExpr(input: unknown): BaseExpr {\n return new ObjectExpr(parseExpressionInput(input));\n}\n\n/** Exposes a ParsedExpression through the Expr methods, SQL rendering, and shared result codec. */\nclass ObjectExpr extends BaseExpr {\n private readonly codec: ResultCodec;\n\n constructor(private readonly parsed: ParsedExpression) {\n super();\n this.codec = chooseExpressionCodec(parsed);\n }\n\n get outputType(): TypeInfo | undefined {\n return this.codec.outputType;\n }\n\n get sqlNullable(): boolean | undefined {\n return expressionNullable(this.parsed);\n }\n\n toSql(ctx: ExprContext): SqlFragment {\n return expressionToSql(this.parsed, ctx, this.codec);\n }\n\n decode(value: unknown): unknown {\n return value == null ? value : this.codec.decode(value);\n }\n\n encode(value: unknown): unknown {\n return value == null ? value : this.codec.encode(value);\n }\n}\n\n/**\n * Reports nullability from the fields of each parsed expression kind.\n * A direct column can become null through a LEFT join, so only independent values prove NOT NULL here.\n */\nexport function expressionNullable(parsed: ParsedExpression): boolean | undefined {\n if (parsed instanceof BaseExpr) return parsed.sqlSource ? undefined : parsed.sqlNullable;\n switch (parsed.kind) {\n case \"literal\":\n return parsed.value === null;\n case \"arrayAgg\":\n return true;\n case \"nullIf\":\n return nullIfNullable();\n case \"coalesce\":\n return coalesceNullable(parsed);\n case \"greatest\":\n return greatestNullable(parsed);\n case \"least\":\n return leastNullable(parsed);\n case \"case\":\n return caseNullable(parsed);\n default:\n return assertNever(parsed);\n }\n}\n\n/**\n * Renders a ParsedExpression in SQL binding order, resolving CASE conditions and collecting references for join pruning.\n * An omitted CASE condition removes its value as well. I.e. an unused Book.title branch must not keep the Book join.\n */\nexport function expressionToSql(parsed: ParsedExpression, ctx: ExprContext, codec: ResultCodec): SqlFragment {\n if (parsed instanceof BaseExpr) return parsed.toSql(ctx);\n switch (parsed.kind) {\n case \"literal\": {\n const dbType = codec.outputType?.dbType;\n return {\n sql: dbType ? `?::${dbType}` : \"?\",\n bindings: [parsed.value === null ? null : codec.encode(parsed.value)],\n refs: [],\n };\n }\n case \"arrayAgg\":\n return arrayAggToSql(parsed, ctx, chooseExpressionCodec(parsed.value));\n case \"coalesce\":\n return coalesceToSql(parsed, ctx, codec);\n case \"nullIf\":\n return nullIfToSql(parsed, ctx, codec);\n case \"greatest\":\n return greatestToSql(parsed, ctx, codec);\n case \"least\":\n return leastToSql(parsed, ctx, codec);\n case \"case\":\n return caseToSql(parsed, ctx, codec);\n default:\n return assertNever(parsed);\n }\n}\n\n/** Renders a function call after its parsed kind has supplied the arguments in SQL order. */\nexport function functionToSql(\n name: string,\n args: readonly ParsedExpression[],\n ctx: ExprContext,\n codec: ResultCodec,\n): SqlFragment {\n const parts = joinFragments(\n args.map((arg) => expressionToSql(arg, ctx, codec)),\n \", \",\n );\n return { ...parts, sql: `${name}(${parts.sql})` };\n}\n"],"mappings":";;;;;;;;;;;;AAiBA,SAAgB,KAAgC,OAAqD;CACnG,OAAO,UAAU,KAAK;AACxB;;AAGA,SAAgB,UAAU,OAA0B;CAClD,OAAO,IAAI,WAAW,qBAAqB,KAAK,CAAC;AACnD;;AAGA,IAAM,aAAN,cAAyB,SAAS;CAGH;CAF7B;CAEA,YAAY,QAA2C;EACrD,MAAM;EADqB,KAAA,SAAA;EAE3B,KAAK,QAAQ,sBAAsB,MAAM;CAC3C;CAEA,IAAI,aAAmC;EACrC,OAAO,KAAK,MAAM;CACpB;CAEA,IAAI,cAAmC;EACrC,OAAO,mBAAmB,KAAK,MAAM;CACvC;CAEA,MAAM,KAA+B;EACnC,OAAO,gBAAgB,KAAK,QAAQ,KAAK,KAAK,KAAK;CACrD;CAEA,OAAO,OAAyB;EAC9B,OAAO,SAAS,OAAO,QAAQ,KAAK,MAAM,OAAO,KAAK;CACxD;CAEA,OAAO,OAAyB;EAC9B,OAAO,SAAS,OAAO,QAAQ,KAAK,MAAM,OAAO,KAAK;CACxD;AACF;;;;;AAMA,SAAgB,mBAAmB,QAA+C;CAChF,IAAI,kBAAkB,UAAU,OAAO,OAAO,YAAY,KAAA,IAAY,OAAO;CAC7E,QAAQ,OAAO,MAAf;EACE,KAAK,WACH,OAAO,OAAO,UAAU;EAC1B,KAAK,YACH,OAAO;EACT,KAAK,UACH,OAAO,eAAe;EACxB,KAAK,YACH,OAAO,iBAAiB,MAAM;EAChC,KAAK,YACH,OAAO,iBAAiB,MAAM;EAChC,KAAK,SACH,OAAO,cAAc,MAAM;EAC7B,KAAK,QACH,OAAO,aAAa,MAAM;EAC5B,SACE,OAAO,YAAY,MAAM;CAC7B;AACF;;;;;AAMA,SAAgB,gBAAgB,QAA0B,KAAkB,OAAiC;CAC3G,IAAI,kBAAkB,UAAU,OAAO,OAAO,MAAM,GAAG;CACvD,QAAQ,OAAO,MAAf;EACE,KAAK,WAAW;GACd,MAAM,SAAS,MAAM,YAAY;GACjC,OAAO;IACL,KAAK,SAAS,MAAM,WAAW;IAC/B,UAAU,CAAC,OAAO,UAAU,OAAO,OAAO,MAAM,OAAO,OAAO,KAAK,CAAC;IACpE,MAAM,CAAC;GACT;EACF;EACA,KAAK,YACH,OAAO,cAAc,QAAQ,KAAK,sBAAsB,OAAO,KAAK,CAAC;EACvE,KAAK,YACH,OAAO,cAAc,QAAQ,KAAK,KAAK;EACzC,KAAK,UACH,OAAO,YAAY,QAAQ,KAAK,KAAK;EACvC,KAAK,YACH,OAAO,cAAc,QAAQ,KAAK,KAAK;EACzC,KAAK,SACH,OAAO,WAAW,QAAQ,KAAK,KAAK;EACtC,KAAK,QACH,OAAO,UAAU,QAAQ,KAAK,KAAK;EACrC,SACE,OAAO,YAAY,MAAM;CAC7B;AACF;;AAGA,SAAgB,cACd,MACA,MACA,KACA,OACa;CACb,MAAM,QAAQ,cACZ,KAAK,KAAK,QAAQ,gBAAgB,KAAK,KAAK,KAAK,CAAC,GAClD,IACF;CACA,OAAO;EAAE,GAAG;EAAO,KAAK,GAAG,KAAK,GAAG,MAAM,IAAI;CAAG;AAClD"}
|
|
@@ -1,11 +1,12 @@
|
|
|
1
1
|
Object.defineProperty(exports, Symbol.toStringTag, { value: "Module" });
|
|
2
2
|
const require_utils = require("../utils.cjs");
|
|
3
3
|
const require_Expr = require("../Expr.cjs");
|
|
4
|
+
const require_expressions_case = require("./case.cjs");
|
|
4
5
|
const require_expressions_coalesce = require("./coalesce.cjs");
|
|
5
6
|
const require_expressions_greatest = require("./greatest.cjs");
|
|
6
7
|
const require_expressions_least = require("./least.cjs");
|
|
7
8
|
const require_expressions_nullIf = require("./nullIf.cjs");
|
|
8
|
-
const
|
|
9
|
+
const require_expressions_arrayAgg = require("./arrayAgg.cjs");
|
|
9
10
|
//#region src/expressions/parseExpression.ts
|
|
10
11
|
const exprNames = [
|
|
11
12
|
"coalesce",
|
|
@@ -15,7 +16,7 @@ const exprNames = [
|
|
|
15
16
|
];
|
|
16
17
|
/** Requires an expression object at the root before parsing its nested operands. */
|
|
17
18
|
function parseExpressionInput(input) {
|
|
18
|
-
if (!isObject(input) || !("case" in input) && !exprNames.some((name) => name in input)) throw new Error("expr expects an object with case, coalesce, nullIf, greatest, or least");
|
|
19
|
+
if (!isObject(input) || !("case" in input) && !("arrayAgg" in input) && !exprNames.some((name) => name in input)) throw new Error("expr expects an object with arrayAgg, case, coalesce, nullIf, greatest, or least");
|
|
19
20
|
return parseExpression(input);
|
|
20
21
|
}
|
|
21
22
|
/**
|
|
@@ -37,6 +38,10 @@ function parseExpression(input) {
|
|
|
37
38
|
default: return require_utils.assertNever(name);
|
|
38
39
|
}
|
|
39
40
|
}
|
|
41
|
+
if (isObject(input) && "arrayAgg" in input) {
|
|
42
|
+
checkKeys(input, ["arrayAgg"]);
|
|
43
|
+
return require_expressions_arrayAgg.parseArrayAggExpression(input.arrayAgg);
|
|
44
|
+
}
|
|
40
45
|
if (isObject(input) && "case" in input) {
|
|
41
46
|
checkKeys(input, ["case"]);
|
|
42
47
|
return require_expressions_case.parseCaseExpression(input.case);
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"parseExpression.cjs","names":["BaseExpr","parseCoalesceExpression","parseNullIfExpression","parseGreatestExpression","parseLeastExpression","assertNever","parseCaseExpression"],"sources":["../../src/expressions/parseExpression.ts"],"sourcesContent":["import { BaseExpr } from \"../Expr.ts\";\nimport { assertNever } from \"../utils.ts\";\nimport { parseCaseExpression } from \"./case.ts\";\nimport { parseCoalesceExpression } from \"./coalesce.ts\";\nimport { parseGreatestExpression } from \"./greatest.ts\";\nimport { parseLeastExpression } from \"./least.ts\";\nimport { parseNullIfExpression } from \"./nullIf.ts\";\nimport type { ExprName, ParsedExpression } from \"./types.ts\";\n\n// These expressions take operand arrays; CASE uses WHEN/THEN entries instead.\nconst exprNames = [\"coalesce\", \"nullIf\", \"greatest\", \"least\"] as const satisfies readonly ExprName[];\n\n/** Requires an expression object at the root before parsing its nested operands. */\nexport function parseExpressionInput(input: unknown): ParsedExpression {\n if (!isObject(input) || (!(\"case\" in input) && !exprNames.some((name) => name in input))) {\n throw new Error(\"expr expects an object with case, coalesce, nullIf, greatest, or least\");\n }\n return parseExpression(input);\n}\n\n/**\n * Parses expression inputs into a ParsedExpression, validating their shape and copying operand arrays.\n * Nested expressions stay together so their literals can use a column's codec from another branch.\n * I.e. COALESCE(Book.id, CASE ... THEN \"b:9\" END) must encode \"b:9\" as an integer.\n */\nexport function parseExpression(input: unknown): ParsedExpression {\n if (input instanceof BaseExpr) return input;\n if (input === undefined) throw new Error(\"Use null for a SQL NULL value\");\n const name = isObject(input) ? exprNames.find((name) => name in input) : undefined;\n if (name && isObject(input)) {\n checkKeys(input, [name]);\n switch (name) {\n case \"coalesce\":\n return parseCoalesceExpression(input.coalesce);\n case \"nullIf\":\n return parseNullIfExpression(input.nullIf);\n case \"greatest\":\n return parseGreatestExpression(input.greatest);\n case \"least\":\n return parseLeastExpression(input.least);\n default:\n return assertNever(name);\n }\n }\n if (isObject(input) && \"case\" in input) {\n checkKeys(input, [\"case\"]);\n return parseCaseExpression(input.case);\n }\n return { kind: \"literal\", value: input };\n}\n\n/** Validates and copies a nonempty operand array, preserving its first element in the parsed type. */\nexport function parseNonEmptyOperands(input: unknown, name: string): [ParsedExpression, ...ParsedExpression[]] {\n if (!Array.isArray(input) || input.length === 0) throw new Error(`${name} needs at least one value`);\n // Validation above guarantees a first operand; keep that guarantee in the parsed type.\n const [first, ...rest] = input;\n return [parseExpression(first), ...rest.map((operand) => parseExpression(operand))];\n}\n\n/** Rejects misspelled or mixed expression keys rather than silently ignoring them. */\nexport function checkKeys(value: object, allowed: string[]): void {\n for (const key of Reflect.ownKeys(value)) {\n if (typeof key !== \"string\" || !allowed.includes(key)) throw new Error(`Unknown expression key '${String(key)}'`);\n }\n}\n\n/** Narrows expression input objects without treating null as an object. */\nexport function isObject(value: unknown): value is Record<string, unknown> {\n return typeof value === \"object\" && value !== null;\n}\n"],"mappings":"
|
|
1
|
+
{"version":3,"file":"parseExpression.cjs","names":["BaseExpr","parseCoalesceExpression","parseNullIfExpression","parseGreatestExpression","parseLeastExpression","assertNever","parseArrayAggExpression","parseCaseExpression"],"sources":["../../src/expressions/parseExpression.ts"],"sourcesContent":["import { BaseExpr } from \"../Expr.ts\";\nimport { assertNever } from \"../utils.ts\";\nimport { parseArrayAggExpression } from \"./arrayAgg.ts\";\nimport { parseCaseExpression } from \"./case.ts\";\nimport { parseCoalesceExpression } from \"./coalesce.ts\";\nimport { parseGreatestExpression } from \"./greatest.ts\";\nimport { parseLeastExpression } from \"./least.ts\";\nimport { parseNullIfExpression } from \"./nullIf.ts\";\nimport type { ExprName, ParsedExpression } from \"./types.ts\";\n\n// These expressions take operand arrays; CASE uses WHEN/THEN entries instead.\nconst exprNames = [\"coalesce\", \"nullIf\", \"greatest\", \"least\"] as const satisfies readonly ExprName[];\n\n/** Requires an expression object at the root before parsing its nested operands. */\nexport function parseExpressionInput(input: unknown): ParsedExpression {\n if (!isObject(input) || (!(\"case\" in input) && !(\"arrayAgg\" in input) && !exprNames.some((name) => name in input))) {\n throw new Error(\"expr expects an object with arrayAgg, case, coalesce, nullIf, greatest, or least\");\n }\n return parseExpression(input);\n}\n\n/**\n * Parses expression inputs into a ParsedExpression, validating their shape and copying operand arrays.\n * Nested expressions stay together so their literals can use a column's codec from another branch.\n * I.e. COALESCE(Book.id, CASE ... THEN \"b:9\" END) must encode \"b:9\" as an integer.\n */\nexport function parseExpression(input: unknown): ParsedExpression {\n if (input instanceof BaseExpr) return input;\n if (input === undefined) throw new Error(\"Use null for a SQL NULL value\");\n const name = isObject(input) ? exprNames.find((name) => name in input) : undefined;\n if (name && isObject(input)) {\n checkKeys(input, [name]);\n switch (name) {\n case \"coalesce\":\n return parseCoalesceExpression(input.coalesce);\n case \"nullIf\":\n return parseNullIfExpression(input.nullIf);\n case \"greatest\":\n return parseGreatestExpression(input.greatest);\n case \"least\":\n return parseLeastExpression(input.least);\n default:\n return assertNever(name);\n }\n }\n if (isObject(input) && \"arrayAgg\" in input) {\n checkKeys(input, [\"arrayAgg\"]);\n return parseArrayAggExpression(input.arrayAgg);\n }\n if (isObject(input) && \"case\" in input) {\n checkKeys(input, [\"case\"]);\n return parseCaseExpression(input.case);\n }\n return { kind: \"literal\", value: input };\n}\n\n/** Validates and copies a nonempty operand array, preserving its first element in the parsed type. */\nexport function parseNonEmptyOperands(input: unknown, name: string): [ParsedExpression, ...ParsedExpression[]] {\n if (!Array.isArray(input) || input.length === 0) throw new Error(`${name} needs at least one value`);\n // Validation above guarantees a first operand; keep that guarantee in the parsed type.\n const [first, ...rest] = input;\n return [parseExpression(first), ...rest.map((operand) => parseExpression(operand))];\n}\n\n/** Rejects misspelled or mixed expression keys rather than silently ignoring them. */\nexport function checkKeys(value: object, allowed: string[]): void {\n for (const key of Reflect.ownKeys(value)) {\n if (typeof key !== \"string\" || !allowed.includes(key)) throw new Error(`Unknown expression key '${String(key)}'`);\n }\n}\n\n/** Narrows expression input objects without treating null as an object. */\nexport function isObject(value: unknown): value is Record<string, unknown> {\n return typeof value === \"object\" && value !== null;\n}\n"],"mappings":";;;;;;;;;;AAWA,MAAM,YAAY;CAAC;CAAY;CAAU;CAAY;AAAO;;AAG5D,SAAgB,qBAAqB,OAAkC;CACrE,IAAI,CAAC,SAAS,KAAK,KAAM,EAAE,UAAU,UAAU,EAAE,cAAc,UAAU,CAAC,UAAU,MAAM,SAAS,QAAQ,KAAK,GAC9G,MAAM,IAAI,MAAM,kFAAkF;CAEpG,OAAO,gBAAgB,KAAK;AAC9B;;;;;;AAOA,SAAgB,gBAAgB,OAAkC;CAChE,IAAI,iBAAiBA,aAAAA,UAAU,OAAO;CACtC,IAAI,UAAU,KAAA,GAAW,MAAM,IAAI,MAAM,+BAA+B;CACxE,MAAM,OAAO,SAAS,KAAK,IAAI,UAAU,MAAM,SAAS,QAAQ,KAAK,IAAI,KAAA;CACzE,IAAI,QAAQ,SAAS,KAAK,GAAG;EAC3B,UAAU,OAAO,CAAC,IAAI,CAAC;EACvB,QAAQ,MAAR;GACE,KAAK,YACH,OAAOC,6BAAAA,wBAAwB,MAAM,QAAQ;GAC/C,KAAK,UACH,OAAOC,2BAAAA,sBAAsB,MAAM,MAAM;GAC3C,KAAK,YACH,OAAOC,6BAAAA,wBAAwB,MAAM,QAAQ;GAC/C,KAAK,SACH,OAAOC,0BAAAA,qBAAqB,MAAM,KAAK;GACzC,SACE,OAAOC,cAAAA,YAAY,IAAI;EAC3B;CACF;CACA,IAAI,SAAS,KAAK,KAAK,cAAc,OAAO;EAC1C,UAAU,OAAO,CAAC,UAAU,CAAC;EAC7B,OAAOC,6BAAAA,wBAAwB,MAAM,QAAQ;CAC/C;CACA,IAAI,SAAS,KAAK,KAAK,UAAU,OAAO;EACtC,UAAU,OAAO,CAAC,MAAM,CAAC;EACzB,OAAOC,yBAAAA,oBAAoB,MAAM,IAAI;CACvC;CACA,OAAO;EAAE,MAAM;EAAW,OAAO;CAAM;AACzC;;AAGA,SAAgB,sBAAsB,OAAgB,MAAyD;CAC7G,IAAI,CAAC,MAAM,QAAQ,KAAK,KAAK,MAAM,WAAW,GAAG,MAAM,IAAI,MAAM,GAAG,KAAK,0BAA0B;CAEnG,MAAM,CAAC,OAAO,GAAG,QAAQ;CACzB,OAAO,CAAC,gBAAgB,KAAK,GAAG,GAAG,KAAK,KAAK,YAAY,gBAAgB,OAAO,CAAC,CAAC;AACpF;;AAGA,SAAgB,UAAU,OAAe,SAAyB;CAChE,KAAK,MAAM,OAAO,QAAQ,QAAQ,KAAK,GACrC,IAAI,OAAO,QAAQ,YAAY,CAAC,QAAQ,SAAS,GAAG,GAAG,MAAM,IAAI,MAAM,2BAA2B,OAAO,GAAG,EAAE,EAAE;AAEpH;;AAGA,SAAgB,SAAS,OAAkD;CACzE,OAAO,OAAO,UAAU,YAAY,UAAU;AAChD"}
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"parseExpression.d.cts","names":[],"sources":["../../src/expressions/parseExpression.ts"],"mappings":";;;
|
|
1
|
+
{"version":3,"file":"parseExpression.d.cts","names":[],"sources":["../../src/expressions/parseExpression.ts"],"mappings":";;;iBAcgB,qBAAqB,iBAAiB;;;;;;iBAYtC,gBAAgB,iBAAiB;;iBA+BjC,sBAAsB,gBAAgB,gBAAgB,qBAAqB;;iBAQ3E,UAAU,eAAe;;iBAOzB,SAAS,iBAAiB,SAAS"}
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"parseExpression.d.mts","names":[],"sources":["../../src/expressions/parseExpression.ts"],"mappings":";;;
|
|
1
|
+
{"version":3,"file":"parseExpression.d.mts","names":[],"sources":["../../src/expressions/parseExpression.ts"],"mappings":";;;iBAcgB,qBAAqB,iBAAiB;;;;;;iBAYtC,gBAAgB,iBAAiB;;iBA+BjC,sBAAsB,gBAAgB,gBAAgB,qBAAqB;;iBAQ3E,UAAU,eAAe;;iBAOzB,SAAS,iBAAiB,SAAS"}
|
|
@@ -1,10 +1,11 @@
|
|
|
1
1
|
import { assertNever } from "../utils.js";
|
|
2
2
|
import { BaseExpr } from "../Expr.js";
|
|
3
|
+
import { parseCaseExpression } from "./case.js";
|
|
3
4
|
import { parseCoalesceExpression } from "./coalesce.js";
|
|
4
5
|
import { parseGreatestExpression } from "./greatest.js";
|
|
5
6
|
import { parseLeastExpression } from "./least.js";
|
|
6
7
|
import { parseNullIfExpression } from "./nullIf.js";
|
|
7
|
-
import {
|
|
8
|
+
import { parseArrayAggExpression } from "./arrayAgg.js";
|
|
8
9
|
//#region src/expressions/parseExpression.ts
|
|
9
10
|
const exprNames = [
|
|
10
11
|
"coalesce",
|
|
@@ -14,7 +15,7 @@ const exprNames = [
|
|
|
14
15
|
];
|
|
15
16
|
/** Requires an expression object at the root before parsing its nested operands. */
|
|
16
17
|
function parseExpressionInput(input) {
|
|
17
|
-
if (!isObject(input) || !("case" in input) && !exprNames.some((name) => name in input)) throw new Error("expr expects an object with case, coalesce, nullIf, greatest, or least");
|
|
18
|
+
if (!isObject(input) || !("case" in input) && !("arrayAgg" in input) && !exprNames.some((name) => name in input)) throw new Error("expr expects an object with arrayAgg, case, coalesce, nullIf, greatest, or least");
|
|
18
19
|
return parseExpression(input);
|
|
19
20
|
}
|
|
20
21
|
/**
|
|
@@ -36,6 +37,10 @@ function parseExpression(input) {
|
|
|
36
37
|
default: return assertNever(name);
|
|
37
38
|
}
|
|
38
39
|
}
|
|
40
|
+
if (isObject(input) && "arrayAgg" in input) {
|
|
41
|
+
checkKeys(input, ["arrayAgg"]);
|
|
42
|
+
return parseArrayAggExpression(input.arrayAgg);
|
|
43
|
+
}
|
|
39
44
|
if (isObject(input) && "case" in input) {
|
|
40
45
|
checkKeys(input, ["case"]);
|
|
41
46
|
return parseCaseExpression(input.case);
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"parseExpression.js","names":[],"sources":["../../src/expressions/parseExpression.ts"],"sourcesContent":["import { BaseExpr } from \"../Expr.ts\";\nimport { assertNever } from \"../utils.ts\";\nimport { parseCaseExpression } from \"./case.ts\";\nimport { parseCoalesceExpression } from \"./coalesce.ts\";\nimport { parseGreatestExpression } from \"./greatest.ts\";\nimport { parseLeastExpression } from \"./least.ts\";\nimport { parseNullIfExpression } from \"./nullIf.ts\";\nimport type { ExprName, ParsedExpression } from \"./types.ts\";\n\n// These expressions take operand arrays; CASE uses WHEN/THEN entries instead.\nconst exprNames = [\"coalesce\", \"nullIf\", \"greatest\", \"least\"] as const satisfies readonly ExprName[];\n\n/** Requires an expression object at the root before parsing its nested operands. */\nexport function parseExpressionInput(input: unknown): ParsedExpression {\n if (!isObject(input) || (!(\"case\" in input) && !exprNames.some((name) => name in input))) {\n throw new Error(\"expr expects an object with case, coalesce, nullIf, greatest, or least\");\n }\n return parseExpression(input);\n}\n\n/**\n * Parses expression inputs into a ParsedExpression, validating their shape and copying operand arrays.\n * Nested expressions stay together so their literals can use a column's codec from another branch.\n * I.e. COALESCE(Book.id, CASE ... THEN \"b:9\" END) must encode \"b:9\" as an integer.\n */\nexport function parseExpression(input: unknown): ParsedExpression {\n if (input instanceof BaseExpr) return input;\n if (input === undefined) throw new Error(\"Use null for a SQL NULL value\");\n const name = isObject(input) ? exprNames.find((name) => name in input) : undefined;\n if (name && isObject(input)) {\n checkKeys(input, [name]);\n switch (name) {\n case \"coalesce\":\n return parseCoalesceExpression(input.coalesce);\n case \"nullIf\":\n return parseNullIfExpression(input.nullIf);\n case \"greatest\":\n return parseGreatestExpression(input.greatest);\n case \"least\":\n return parseLeastExpression(input.least);\n default:\n return assertNever(name);\n }\n }\n if (isObject(input) && \"case\" in input) {\n checkKeys(input, [\"case\"]);\n return parseCaseExpression(input.case);\n }\n return { kind: \"literal\", value: input };\n}\n\n/** Validates and copies a nonempty operand array, preserving its first element in the parsed type. */\nexport function parseNonEmptyOperands(input: unknown, name: string): [ParsedExpression, ...ParsedExpression[]] {\n if (!Array.isArray(input) || input.length === 0) throw new Error(`${name} needs at least one value`);\n // Validation above guarantees a first operand; keep that guarantee in the parsed type.\n const [first, ...rest] = input;\n return [parseExpression(first), ...rest.map((operand) => parseExpression(operand))];\n}\n\n/** Rejects misspelled or mixed expression keys rather than silently ignoring them. */\nexport function checkKeys(value: object, allowed: string[]): void {\n for (const key of Reflect.ownKeys(value)) {\n if (typeof key !== \"string\" || !allowed.includes(key)) throw new Error(`Unknown expression key '${String(key)}'`);\n }\n}\n\n/** Narrows expression input objects without treating null as an object. */\nexport function isObject(value: unknown): value is Record<string, unknown> {\n return typeof value === \"object\" && value !== null;\n}\n"],"mappings":"
|
|
1
|
+
{"version":3,"file":"parseExpression.js","names":[],"sources":["../../src/expressions/parseExpression.ts"],"sourcesContent":["import { BaseExpr } from \"../Expr.ts\";\nimport { assertNever } from \"../utils.ts\";\nimport { parseArrayAggExpression } from \"./arrayAgg.ts\";\nimport { parseCaseExpression } from \"./case.ts\";\nimport { parseCoalesceExpression } from \"./coalesce.ts\";\nimport { parseGreatestExpression } from \"./greatest.ts\";\nimport { parseLeastExpression } from \"./least.ts\";\nimport { parseNullIfExpression } from \"./nullIf.ts\";\nimport type { ExprName, ParsedExpression } from \"./types.ts\";\n\n// These expressions take operand arrays; CASE uses WHEN/THEN entries instead.\nconst exprNames = [\"coalesce\", \"nullIf\", \"greatest\", \"least\"] as const satisfies readonly ExprName[];\n\n/** Requires an expression object at the root before parsing its nested operands. */\nexport function parseExpressionInput(input: unknown): ParsedExpression {\n if (!isObject(input) || (!(\"case\" in input) && !(\"arrayAgg\" in input) && !exprNames.some((name) => name in input))) {\n throw new Error(\"expr expects an object with arrayAgg, case, coalesce, nullIf, greatest, or least\");\n }\n return parseExpression(input);\n}\n\n/**\n * Parses expression inputs into a ParsedExpression, validating their shape and copying operand arrays.\n * Nested expressions stay together so their literals can use a column's codec from another branch.\n * I.e. COALESCE(Book.id, CASE ... THEN \"b:9\" END) must encode \"b:9\" as an integer.\n */\nexport function parseExpression(input: unknown): ParsedExpression {\n if (input instanceof BaseExpr) return input;\n if (input === undefined) throw new Error(\"Use null for a SQL NULL value\");\n const name = isObject(input) ? exprNames.find((name) => name in input) : undefined;\n if (name && isObject(input)) {\n checkKeys(input, [name]);\n switch (name) {\n case \"coalesce\":\n return parseCoalesceExpression(input.coalesce);\n case \"nullIf\":\n return parseNullIfExpression(input.nullIf);\n case \"greatest\":\n return parseGreatestExpression(input.greatest);\n case \"least\":\n return parseLeastExpression(input.least);\n default:\n return assertNever(name);\n }\n }\n if (isObject(input) && \"arrayAgg\" in input) {\n checkKeys(input, [\"arrayAgg\"]);\n return parseArrayAggExpression(input.arrayAgg);\n }\n if (isObject(input) && \"case\" in input) {\n checkKeys(input, [\"case\"]);\n return parseCaseExpression(input.case);\n }\n return { kind: \"literal\", value: input };\n}\n\n/** Validates and copies a nonempty operand array, preserving its first element in the parsed type. */\nexport function parseNonEmptyOperands(input: unknown, name: string): [ParsedExpression, ...ParsedExpression[]] {\n if (!Array.isArray(input) || input.length === 0) throw new Error(`${name} needs at least one value`);\n // Validation above guarantees a first operand; keep that guarantee in the parsed type.\n const [first, ...rest] = input;\n return [parseExpression(first), ...rest.map((operand) => parseExpression(operand))];\n}\n\n/** Rejects misspelled or mixed expression keys rather than silently ignoring them. */\nexport function checkKeys(value: object, allowed: string[]): void {\n for (const key of Reflect.ownKeys(value)) {\n if (typeof key !== \"string\" || !allowed.includes(key)) throw new Error(`Unknown expression key '${String(key)}'`);\n }\n}\n\n/** Narrows expression input objects without treating null as an object. */\nexport function isObject(value: unknown): value is Record<string, unknown> {\n return typeof value === \"object\" && value !== null;\n}\n"],"mappings":";;;;;;;;;AAWA,MAAM,YAAY;CAAC;CAAY;CAAU;CAAY;AAAO;;AAG5D,SAAgB,qBAAqB,OAAkC;CACrE,IAAI,CAAC,SAAS,KAAK,KAAM,EAAE,UAAU,UAAU,EAAE,cAAc,UAAU,CAAC,UAAU,MAAM,SAAS,QAAQ,KAAK,GAC9G,MAAM,IAAI,MAAM,kFAAkF;CAEpG,OAAO,gBAAgB,KAAK;AAC9B;;;;;;AAOA,SAAgB,gBAAgB,OAAkC;CAChE,IAAI,iBAAiB,UAAU,OAAO;CACtC,IAAI,UAAU,KAAA,GAAW,MAAM,IAAI,MAAM,+BAA+B;CACxE,MAAM,OAAO,SAAS,KAAK,IAAI,UAAU,MAAM,SAAS,QAAQ,KAAK,IAAI,KAAA;CACzE,IAAI,QAAQ,SAAS,KAAK,GAAG;EAC3B,UAAU,OAAO,CAAC,IAAI,CAAC;EACvB,QAAQ,MAAR;GACE,KAAK,YACH,OAAO,wBAAwB,MAAM,QAAQ;GAC/C,KAAK,UACH,OAAO,sBAAsB,MAAM,MAAM;GAC3C,KAAK,YACH,OAAO,wBAAwB,MAAM,QAAQ;GAC/C,KAAK,SACH,OAAO,qBAAqB,MAAM,KAAK;GACzC,SACE,OAAO,YAAY,IAAI;EAC3B;CACF;CACA,IAAI,SAAS,KAAK,KAAK,cAAc,OAAO;EAC1C,UAAU,OAAO,CAAC,UAAU,CAAC;EAC7B,OAAO,wBAAwB,MAAM,QAAQ;CAC/C;CACA,IAAI,SAAS,KAAK,KAAK,UAAU,OAAO;EACtC,UAAU,OAAO,CAAC,MAAM,CAAC;EACzB,OAAO,oBAAoB,MAAM,IAAI;CACvC;CACA,OAAO;EAAE,MAAM;EAAW,OAAO;CAAM;AACzC;;AAGA,SAAgB,sBAAsB,OAAgB,MAAyD;CAC7G,IAAI,CAAC,MAAM,QAAQ,KAAK,KAAK,MAAM,WAAW,GAAG,MAAM,IAAI,MAAM,GAAG,KAAK,0BAA0B;CAEnG,MAAM,CAAC,OAAO,GAAG,QAAQ;CACzB,OAAO,CAAC,gBAAgB,KAAK,GAAG,GAAG,KAAK,KAAK,YAAY,gBAAgB,OAAO,CAAC,CAAC;AACpF;;AAGA,SAAgB,UAAU,OAAe,SAAyB;CAChE,KAAK,MAAM,OAAO,QAAQ,QAAQ,KAAK,GACrC,IAAI,OAAO,QAAQ,YAAY,CAAC,QAAQ,SAAS,GAAG,GAAG,MAAM,IAAI,MAAM,2BAA2B,OAAO,GAAG,EAAE,EAAE;AAEpH;;AAGA,SAAgB,SAAS,OAAkD;CACzE,OAAO,OAAO,UAAU,YAAY,UAAU;AAChD"}
|
|
@@ -1,3 +1,4 @@
|
|
|
1
|
+
import { ArrayAggExpressionOptions, ArrayAggInput, ParsedArrayAggExpression } from "./arrayAgg.cjs";
|
|
1
2
|
import { CaseArmValue, CaseInput, CheckCase, ParsedCaseExpression } from "./case.cjs";
|
|
2
3
|
import { CoalesceInput, ParsedCoalesceExpression } from "./coalesce.cjs";
|
|
3
4
|
import { GreatestInput, ParsedGreatestExpression } from "./greatest.cjs";
|
|
@@ -9,12 +10,13 @@ import { BaseExpr, Expr, ExprBrand, ExprLike, exprBrand } from "../Expr.cjs";
|
|
|
9
10
|
/** Each operation owns its input fields; the combined input permits exactly one operation. */
|
|
10
11
|
interface ExprInputs {
|
|
11
12
|
case: CaseInput;
|
|
13
|
+
arrayAgg: ArrayAggInput;
|
|
12
14
|
coalesce: CoalesceInput;
|
|
13
15
|
nullIf: NullIfInput;
|
|
14
16
|
greatest: GreatestInput;
|
|
15
17
|
least: LeastInput;
|
|
16
18
|
}
|
|
17
|
-
type ExprName = Exclude<keyof ExprInputs, "case">;
|
|
19
|
+
type ExprName = Exclude<keyof ExprInputs, "case" | "arrayAgg">;
|
|
18
20
|
type ExprArgsInput = { [K in ExprName]: { readonly [P in K]: readonly unknown[]; }; }[ExprName];
|
|
19
21
|
type ExprArgs<V> = Extract<V[keyof V & ExprName], readonly unknown[]>;
|
|
20
22
|
/** An expression object used in select or passed to expr, i.e. { coalesce: [a.last_name, a.first_name] }. */
|
|
@@ -32,7 +34,9 @@ type ExpressionValue<V, J extends QueryJoins> = unknown extends V ? V : V extend
|
|
|
32
34
|
readonly [inputBrand]: infer I;
|
|
33
35
|
} ? InputResult<I, J> : V extends {
|
|
34
36
|
readonly [exprBrand]: ExprBrand<infer R, infer S>;
|
|
35
|
-
} ? MaybeNull<R, S, J> : V extends
|
|
37
|
+
} ? MaybeNull<R, S, J> : V extends {
|
|
38
|
+
readonly arrayAgg: infer A;
|
|
39
|
+
} ? ExpressionValue<ArrayAggValue<A>, J>[] | null : V extends ExprArgsInput ? V extends {
|
|
36
40
|
readonly nullIf: readonly unknown[];
|
|
37
41
|
} ? ExpressionValue<ExprArgs<V>[0], J> | null : V extends {
|
|
38
42
|
readonly coalesce: readonly unknown[];
|
|
@@ -49,7 +53,9 @@ type WidenLiteral<V> = V extends string ? string : V extends number ? number : V
|
|
|
49
53
|
*/
|
|
50
54
|
type InputResult<I, J extends QueryJoins> = [Exclude<BranchResult<I>, null>] extends [never] ? ExpressionValue<I, J> : Exclude<BranchResult<I>, null> | (null extends ExpressionValue<I, J> ? null : never);
|
|
51
55
|
/** Collects the types supplied by columns and existing expressions, excluding plain literal fallbacks. */
|
|
52
|
-
type BranchResult<V> = unknown extends V ? never : V extends ExprLike<infer R> ? R : V extends
|
|
56
|
+
type BranchResult<V> = unknown extends V ? never : V extends ExprLike<infer R> ? R : V extends {
|
|
57
|
+
readonly arrayAgg: infer A;
|
|
58
|
+
} ? BranchResult<ArrayAggValue<A>>[] : V extends ExprArgsInput ? BranchResult<V extends {
|
|
53
59
|
readonly nullIf: readonly unknown[];
|
|
54
60
|
} ? ExprArgs<V>[0] : ExprArgs<V>[number]> : V extends {
|
|
55
61
|
readonly case: infer A;
|
|
@@ -57,7 +63,9 @@ type BranchResult<V> = unknown extends V ? never : V extends ExprLike<infer R> ?
|
|
|
57
63
|
/** A fixed non-null candidate guarantees a result; a possibly empty candidate array does not. */
|
|
58
64
|
type CoalesceValue<A extends readonly unknown[], J extends QueryJoins> = A extends readonly [infer H, ...infer T] ? Exclude<ExpressionValue<H, J>, null> | (null extends ExpressionValue<H, J> ? CoalesceValue<T, J> : never) : A extends readonly [...infer Before, infer Last] ? Exclude<ExpressionValue<A[number], J>, null> | (null extends ExpressionValue<Last, J> ? (null extends CoalesceValue<Before, J> ? null : never) : never) : ExpressionValue<A[number], J> | null;
|
|
59
65
|
/** Conditions affect which value is chosen, but do not make that value nullable through a LEFT join. */
|
|
60
|
-
type ExpressionSources<V> = unknown extends V ? string : V extends ExprLike<unknown> ? V[typeof exprBrand]["__source"] : V extends
|
|
66
|
+
type ExpressionSources<V> = unknown extends V ? string : V extends ExprLike<unknown> ? V[typeof exprBrand]["__source"] : V extends {
|
|
67
|
+
readonly arrayAgg: infer A;
|
|
68
|
+
} ? ExpressionSources<ArrayAggValue<A>> : V extends ExprArgsInput ? ExpressionSources<ExprArgs<V>[number]> : V extends {
|
|
61
69
|
readonly case: infer A;
|
|
62
70
|
} ? ExpressionSources<CaseArmValue<A>> : never;
|
|
63
71
|
/**
|
|
@@ -67,7 +75,11 @@ type ExpressionSources<V> = unknown extends V ? string : V extends ExprLike<unkn
|
|
|
67
75
|
*/
|
|
68
76
|
type CompatibleValues<V, All = V> = false extends (V extends unknown ? All extends unknown ? CompatibleValue<ExpressionValue<V, []>, ExpressionValue<All, []>> : never : never) ? "Expression values must have compatible types" : unknown;
|
|
69
77
|
/** Checks nested expression shapes and literal fallbacks in one pass; R is the type supplied by existing expressions. */
|
|
70
|
-
type CheckExpression<V, R> = V extends ExprLike<unknown> ? unknown : V extends
|
|
78
|
+
type CheckExpression<V, R> = V extends ExprLike<unknown> ? unknown : V extends {
|
|
79
|
+
readonly arrayAgg: infer A;
|
|
80
|
+
} ? {
|
|
81
|
+
readonly arrayAgg: CheckArrayAgg<A, R extends readonly (infer E)[] ? E : never>;
|
|
82
|
+
} & { readonly [K in Exclude<keyof V, "arrayAgg">]: never; } : V extends ExprArgsInput ? V extends ExprInput ? { readonly [K in keyof V]: K extends ExprName ? V[K] extends readonly unknown[] ? CheckOperands<V[K], K, R> : never : never; } : never : V extends {
|
|
71
83
|
readonly case: infer A;
|
|
72
84
|
} ? {
|
|
73
85
|
readonly case: CheckCase<A, R>;
|
|
@@ -81,7 +93,7 @@ type CheckInput<I> = [I] extends [CheckExpression<I, Exclude<BranchResult<I>, nu
|
|
|
81
93
|
* Existing Expr instances remain intact; literal operands and CASE results are parsed recursively.
|
|
82
94
|
* CASE conditions retain their query input until SQL rendering supplies the alias context.
|
|
83
95
|
*/
|
|
84
|
-
type ParsedExpression = BaseExpr | ParsedLiteralExpression | ParsedCoalesceExpression | ParsedNullIfExpression | ParsedGreatestExpression | ParsedLeastExpression | ParsedCaseExpression;
|
|
96
|
+
type ParsedExpression = BaseExpr | ParsedLiteralExpression | ParsedArrayAggExpression | ParsedCoalesceExpression | ParsedNullIfExpression | ParsedGreatestExpression | ParsedLeastExpression | ParsedCaseExpression;
|
|
85
97
|
/** A bound value whose encoder is selected from the surrounding expression. */
|
|
86
98
|
interface ParsedLiteralExpression {
|
|
87
99
|
kind: "literal";
|
|
@@ -89,6 +101,14 @@ interface ParsedLiteralExpression {
|
|
|
89
101
|
}
|
|
90
102
|
/** The conversions shared by all result values, separate from their SQL rendering. */
|
|
91
103
|
type ResultCodec = Pick<BaseExpr, "outputType" | "encode" | "decode">;
|
|
104
|
+
/** Extracts the aggregate value from compact or expanded ARRAY_AGG input. */
|
|
105
|
+
type ArrayAggValue<A> = A extends {
|
|
106
|
+
readonly value: infer V;
|
|
107
|
+
} ? V : A;
|
|
108
|
+
/** Checks the aggregate value and rejects unknown expanded option keys. */
|
|
109
|
+
type CheckArrayAgg<A, R> = A extends {
|
|
110
|
+
readonly value: unknown;
|
|
111
|
+
} ? A extends ArrayAggExpressionOptions ? { readonly [K in keyof A]: K extends "value" ? CheckExpression<A[K], R> : K extends keyof ArrayAggExpressionOptions ? A[K] : never; } : never : CheckExpression<A, R>;
|
|
92
112
|
//#endregion
|
|
93
113
|
export { CheckExpression, CheckInput, ExprFromInput, ExprInput, ExprName, ExpressionSources, ExpressionValue, ParsedExpression, ParsedLiteralExpression, ResultCodec };
|
|
94
114
|
//# sourceMappingURL=types.d.cts.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"types.d.cts","names":[],"sources":["../../src/expressions/types.ts"],"mappings":"
|
|
1
|
+
{"version":3,"file":"types.d.cts","names":[],"sources":["../../src/expressions/types.ts"],"mappings":"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"}
|
|
@@ -1,3 +1,4 @@
|
|
|
1
|
+
import { ArrayAggExpressionOptions, ArrayAggInput, ParsedArrayAggExpression } from "./arrayAgg.mjs";
|
|
1
2
|
import { CaseArmValue, CaseInput, CheckCase, ParsedCaseExpression } from "./case.mjs";
|
|
2
3
|
import { CoalesceInput, ParsedCoalesceExpression } from "./coalesce.mjs";
|
|
3
4
|
import { GreatestInput, ParsedGreatestExpression } from "./greatest.mjs";
|
|
@@ -9,12 +10,13 @@ import { BaseExpr, Expr, ExprBrand, ExprLike, exprBrand } from "../Expr.mjs";
|
|
|
9
10
|
/** Each operation owns its input fields; the combined input permits exactly one operation. */
|
|
10
11
|
interface ExprInputs {
|
|
11
12
|
case: CaseInput;
|
|
13
|
+
arrayAgg: ArrayAggInput;
|
|
12
14
|
coalesce: CoalesceInput;
|
|
13
15
|
nullIf: NullIfInput;
|
|
14
16
|
greatest: GreatestInput;
|
|
15
17
|
least: LeastInput;
|
|
16
18
|
}
|
|
17
|
-
type ExprName = Exclude<keyof ExprInputs, "case">;
|
|
19
|
+
type ExprName = Exclude<keyof ExprInputs, "case" | "arrayAgg">;
|
|
18
20
|
type ExprArgsInput = { [K in ExprName]: { readonly [P in K]: readonly unknown[]; }; }[ExprName];
|
|
19
21
|
type ExprArgs<V> = Extract<V[keyof V & ExprName], readonly unknown[]>;
|
|
20
22
|
/** An expression object used in select or passed to expr, i.e. { coalesce: [a.last_name, a.first_name] }. */
|
|
@@ -32,7 +34,9 @@ type ExpressionValue<V, J extends QueryJoins> = unknown extends V ? V : V extend
|
|
|
32
34
|
readonly [inputBrand]: infer I;
|
|
33
35
|
} ? InputResult<I, J> : V extends {
|
|
34
36
|
readonly [exprBrand]: ExprBrand<infer R, infer S>;
|
|
35
|
-
} ? MaybeNull<R, S, J> : V extends
|
|
37
|
+
} ? MaybeNull<R, S, J> : V extends {
|
|
38
|
+
readonly arrayAgg: infer A;
|
|
39
|
+
} ? ExpressionValue<ArrayAggValue<A>, J>[] | null : V extends ExprArgsInput ? V extends {
|
|
36
40
|
readonly nullIf: readonly unknown[];
|
|
37
41
|
} ? ExpressionValue<ExprArgs<V>[0], J> | null : V extends {
|
|
38
42
|
readonly coalesce: readonly unknown[];
|
|
@@ -49,7 +53,9 @@ type WidenLiteral<V> = V extends string ? string : V extends number ? number : V
|
|
|
49
53
|
*/
|
|
50
54
|
type InputResult<I, J extends QueryJoins> = [Exclude<BranchResult<I>, null>] extends [never] ? ExpressionValue<I, J> : Exclude<BranchResult<I>, null> | (null extends ExpressionValue<I, J> ? null : never);
|
|
51
55
|
/** Collects the types supplied by columns and existing expressions, excluding plain literal fallbacks. */
|
|
52
|
-
type BranchResult<V> = unknown extends V ? never : V extends ExprLike<infer R> ? R : V extends
|
|
56
|
+
type BranchResult<V> = unknown extends V ? never : V extends ExprLike<infer R> ? R : V extends {
|
|
57
|
+
readonly arrayAgg: infer A;
|
|
58
|
+
} ? BranchResult<ArrayAggValue<A>>[] : V extends ExprArgsInput ? BranchResult<V extends {
|
|
53
59
|
readonly nullIf: readonly unknown[];
|
|
54
60
|
} ? ExprArgs<V>[0] : ExprArgs<V>[number]> : V extends {
|
|
55
61
|
readonly case: infer A;
|
|
@@ -57,7 +63,9 @@ type BranchResult<V> = unknown extends V ? never : V extends ExprLike<infer R> ?
|
|
|
57
63
|
/** A fixed non-null candidate guarantees a result; a possibly empty candidate array does not. */
|
|
58
64
|
type CoalesceValue<A extends readonly unknown[], J extends QueryJoins> = A extends readonly [infer H, ...infer T] ? Exclude<ExpressionValue<H, J>, null> | (null extends ExpressionValue<H, J> ? CoalesceValue<T, J> : never) : A extends readonly [...infer Before, infer Last] ? Exclude<ExpressionValue<A[number], J>, null> | (null extends ExpressionValue<Last, J> ? (null extends CoalesceValue<Before, J> ? null : never) : never) : ExpressionValue<A[number], J> | null;
|
|
59
65
|
/** Conditions affect which value is chosen, but do not make that value nullable through a LEFT join. */
|
|
60
|
-
type ExpressionSources<V> = unknown extends V ? string : V extends ExprLike<unknown> ? V[typeof exprBrand]["__source"] : V extends
|
|
66
|
+
type ExpressionSources<V> = unknown extends V ? string : V extends ExprLike<unknown> ? V[typeof exprBrand]["__source"] : V extends {
|
|
67
|
+
readonly arrayAgg: infer A;
|
|
68
|
+
} ? ExpressionSources<ArrayAggValue<A>> : V extends ExprArgsInput ? ExpressionSources<ExprArgs<V>[number]> : V extends {
|
|
61
69
|
readonly case: infer A;
|
|
62
70
|
} ? ExpressionSources<CaseArmValue<A>> : never;
|
|
63
71
|
/**
|
|
@@ -67,7 +75,11 @@ type ExpressionSources<V> = unknown extends V ? string : V extends ExprLike<unkn
|
|
|
67
75
|
*/
|
|
68
76
|
type CompatibleValues<V, All = V> = false extends (V extends unknown ? All extends unknown ? CompatibleValue<ExpressionValue<V, []>, ExpressionValue<All, []>> : never : never) ? "Expression values must have compatible types" : unknown;
|
|
69
77
|
/** Checks nested expression shapes and literal fallbacks in one pass; R is the type supplied by existing expressions. */
|
|
70
|
-
type CheckExpression<V, R> = V extends ExprLike<unknown> ? unknown : V extends
|
|
78
|
+
type CheckExpression<V, R> = V extends ExprLike<unknown> ? unknown : V extends {
|
|
79
|
+
readonly arrayAgg: infer A;
|
|
80
|
+
} ? {
|
|
81
|
+
readonly arrayAgg: CheckArrayAgg<A, R extends readonly (infer E)[] ? E : never>;
|
|
82
|
+
} & { readonly [K in Exclude<keyof V, "arrayAgg">]: never; } : V extends ExprArgsInput ? V extends ExprInput ? { readonly [K in keyof V]: K extends ExprName ? V[K] extends readonly unknown[] ? CheckOperands<V[K], K, R> : never : never; } : never : V extends {
|
|
71
83
|
readonly case: infer A;
|
|
72
84
|
} ? {
|
|
73
85
|
readonly case: CheckCase<A, R>;
|
|
@@ -81,7 +93,7 @@ type CheckInput<I> = [I] extends [CheckExpression<I, Exclude<BranchResult<I>, nu
|
|
|
81
93
|
* Existing Expr instances remain intact; literal operands and CASE results are parsed recursively.
|
|
82
94
|
* CASE conditions retain their query input until SQL rendering supplies the alias context.
|
|
83
95
|
*/
|
|
84
|
-
type ParsedExpression = BaseExpr | ParsedLiteralExpression | ParsedCoalesceExpression | ParsedNullIfExpression | ParsedGreatestExpression | ParsedLeastExpression | ParsedCaseExpression;
|
|
96
|
+
type ParsedExpression = BaseExpr | ParsedLiteralExpression | ParsedArrayAggExpression | ParsedCoalesceExpression | ParsedNullIfExpression | ParsedGreatestExpression | ParsedLeastExpression | ParsedCaseExpression;
|
|
85
97
|
/** A bound value whose encoder is selected from the surrounding expression. */
|
|
86
98
|
interface ParsedLiteralExpression {
|
|
87
99
|
kind: "literal";
|
|
@@ -89,6 +101,14 @@ interface ParsedLiteralExpression {
|
|
|
89
101
|
}
|
|
90
102
|
/** The conversions shared by all result values, separate from their SQL rendering. */
|
|
91
103
|
type ResultCodec = Pick<BaseExpr, "outputType" | "encode" | "decode">;
|
|
104
|
+
/** Extracts the aggregate value from compact or expanded ARRAY_AGG input. */
|
|
105
|
+
type ArrayAggValue<A> = A extends {
|
|
106
|
+
readonly value: infer V;
|
|
107
|
+
} ? V : A;
|
|
108
|
+
/** Checks the aggregate value and rejects unknown expanded option keys. */
|
|
109
|
+
type CheckArrayAgg<A, R> = A extends {
|
|
110
|
+
readonly value: unknown;
|
|
111
|
+
} ? A extends ArrayAggExpressionOptions ? { readonly [K in keyof A]: K extends "value" ? CheckExpression<A[K], R> : K extends keyof ArrayAggExpressionOptions ? A[K] : never; } : never : CheckExpression<A, R>;
|
|
92
112
|
//#endregion
|
|
93
113
|
export { CheckExpression, CheckInput, ExprFromInput, ExprInput, ExprName, ExpressionSources, ExpressionValue, ParsedExpression, ParsedLiteralExpression, ResultCodec };
|
|
94
114
|
//# sourceMappingURL=types.d.mts.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"types.d.mts","names":[],"sources":["../../src/expressions/types.ts"],"mappings":"
|
|
1
|
+
{"version":3,"file":"types.d.mts","names":[],"sources":["../../src/expressions/types.ts"],"mappings":"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"}
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "joist-core",
|
|
3
|
-
"version": "2.3.0-next.
|
|
3
|
+
"version": "2.3.0-next.93",
|
|
4
4
|
"license": "MIT",
|
|
5
5
|
"type": "module",
|
|
6
6
|
"repository": {
|
|
@@ -42,7 +42,7 @@
|
|
|
42
42
|
"build"
|
|
43
43
|
],
|
|
44
44
|
"peerDependencies": {
|
|
45
|
-
"joist-utils": "2.3.0-next.
|
|
45
|
+
"joist-utils": "2.3.0-next.93"
|
|
46
46
|
},
|
|
47
47
|
"dependencies": {
|
|
48
48
|
"ansis": "^4.3.1",
|