joist-core 2.3.0-next.90 → 2.3.0-next.91
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/query.cjs +141 -10
- package/build/query.cjs.map +1 -1
- package/build/query.d.cts +1 -8
- package/build/query.d.cts.map +1 -1
- package/build/query.d.mts +1 -8
- package/build/query.d.mts.map +1 -1
- package/build/query.js +142 -11
- package/build/query.js.map +1 -1
- package/package.json +2 -2
package/build/query.cjs
CHANGED
|
@@ -1,6 +1,5 @@
|
|
|
1
1
|
Object.defineProperty(exports, Symbol.toStringTag, { value: "Module" });
|
|
2
2
|
const require_conditions = require("./conditions.cjs");
|
|
3
|
-
require("./typeMap.cjs");
|
|
4
3
|
const require_keywords = require("./keywords.cjs");
|
|
5
4
|
const require_EntityMetadata = require("./EntityMetadata.cjs");
|
|
6
5
|
const require_utils = require("./utils.cjs");
|
|
@@ -413,10 +412,6 @@ function validateReadQuery(arg) {
|
|
|
413
412
|
else {
|
|
414
413
|
validateQueryKeys(arg, READ_KEYS, "Read queries");
|
|
415
414
|
if (!("from" in arg && "select" in arg)) require_utils.fail("em.query expects a { from, select, ... } object or a query(...) value");
|
|
416
|
-
if (require_Tables.isTable(arg.select)) {
|
|
417
|
-
const meta = require_Tables.getTableMetadata(arg.select);
|
|
418
|
-
if (meta.inheritanceType || meta.baseType || meta.baseTypes.length || meta.subTypes.length) require_utils.fail(`Inherited table ${meta.type} cannot be selected as entities; select its columns individually`);
|
|
419
|
-
}
|
|
420
415
|
}
|
|
421
416
|
const options = arg;
|
|
422
417
|
for (const key of ["limit", "offset"]) {
|
|
@@ -777,7 +772,7 @@ function parseQuery(q, parent, assigner, recursiveSelf) {
|
|
|
777
772
|
});
|
|
778
773
|
const softDeletes = q.softDeletes ?? "exclude";
|
|
779
774
|
const from = parseFrom();
|
|
780
|
-
const
|
|
775
|
+
const userJoins = pendingJoins.map((j) => {
|
|
781
776
|
const source = j.parseSource();
|
|
782
777
|
const userOn = conditionToSql(j.on, ctx, true);
|
|
783
778
|
const injected = injectedConditions(source, j.softDeletes ? softDeletes : "include");
|
|
@@ -790,8 +785,11 @@ function parseQuery(q, parent, assigner, recursiveSelf) {
|
|
|
790
785
|
fullOn
|
|
791
786
|
};
|
|
792
787
|
});
|
|
793
|
-
const
|
|
794
|
-
const
|
|
788
|
+
const cti = ctiEntityPlan(q, from, assigner);
|
|
789
|
+
const joins = [...cti.joins, ...userJoins];
|
|
790
|
+
const sti = stiEntityPlan(q, from);
|
|
791
|
+
const { selects, decodeRows, output } = selectsToSql(q, ctx, from, cti.selects ?? sti.selects);
|
|
792
|
+
const fromInjected = [...injectedConditions(from, softDeletes), ...sti.conditions];
|
|
795
793
|
const where = conditionToSql(fromInjected.length > 0 ? { and: [q.where, ...fromInjected] } : q.where, ctx, true);
|
|
796
794
|
const having = conditionToSql(q.having, ctx, true);
|
|
797
795
|
const groupBys = (q.groupBy ?? []).map((g) => asExpr(g, "groupBy").toSql(ctx));
|
|
@@ -963,14 +961,14 @@ function registerJoinTable(handle, ctx, assigner) {
|
|
|
963
961
|
});
|
|
964
962
|
}
|
|
965
963
|
/** Generates the `select` clause SQL and returns how to decode the resulting rows. */
|
|
966
|
-
function selectsToSql(q, ctx, from) {
|
|
964
|
+
function selectsToSql(q, ctx, from, entitySelects) {
|
|
967
965
|
const { select } = q;
|
|
968
966
|
if (require_Tables.isTable(select)) {
|
|
969
967
|
if (from.handle !== require_Tables.getTableMgmt(select)) require_utils.fail("Selecting a joined table is not supported yet; select the from table, or select its columns individually");
|
|
970
968
|
const alias = ctx.aliasFor(require_Tables.getTableMgmt(select));
|
|
971
969
|
const meta = require_Tables.getTableMetadata(select);
|
|
972
970
|
return {
|
|
973
|
-
selects: from.entitySelects.map((s) => ({
|
|
971
|
+
selects: entitySelects ?? from.entitySelects.map((s) => ({
|
|
974
972
|
sql: s,
|
|
975
973
|
bindings: [],
|
|
976
974
|
refs: [alias]
|
|
@@ -1000,6 +998,139 @@ function selectsToSql(q, ctx, from) {
|
|
|
1000
998
|
projection.output = joinedOutput(projection.output, q.join);
|
|
1001
999
|
return projection;
|
|
1002
1000
|
}
|
|
1001
|
+
/**
|
|
1002
|
+
* Builds the mandatory CTI joins and flat row projection needed by entity hydration.
|
|
1003
|
+
*
|
|
1004
|
+
* The joins use the same ParsedJoin path as user joins, but `keep: true` makes them survive pruning.
|
|
1005
|
+
* They are generated only for an entity selection of the from source; POJO and scalar reads keep their
|
|
1006
|
+
* physical-table behavior. User source aliases have already been allocated, so the preferred `_bN` and
|
|
1007
|
+
* `_sN` aliases can be made unique before their projection fragments are built.
|
|
1008
|
+
*/
|
|
1009
|
+
function ctiEntityPlan(q, from, assigner) {
|
|
1010
|
+
if (!require_Tables.isTable(q.select) || from.handle !== require_Tables.getTableMgmt(q.select)) return {
|
|
1011
|
+
joins: [],
|
|
1012
|
+
selects: void 0
|
|
1013
|
+
};
|
|
1014
|
+
const meta = require_Tables.getTableMetadata(q.select);
|
|
1015
|
+
if (meta.inheritanceType !== "cti") return {
|
|
1016
|
+
joins: [],
|
|
1017
|
+
selects: void 0
|
|
1018
|
+
};
|
|
1019
|
+
const joins = [];
|
|
1020
|
+
const selects = from.entitySelects.map((sql) => ({
|
|
1021
|
+
sql,
|
|
1022
|
+
bindings: [],
|
|
1023
|
+
refs: [from.alias]
|
|
1024
|
+
}));
|
|
1025
|
+
for (const [i, baseMeta] of meta.baseTypes.entries()) {
|
|
1026
|
+
const alias = assigner.getLiteralAlias(`${from.alias}_b${i}`);
|
|
1027
|
+
joins.push(ctiJoin(from, baseMeta, alias));
|
|
1028
|
+
selects.push(...entitySelectFragments(baseMeta, alias));
|
|
1029
|
+
}
|
|
1030
|
+
const subtypeColumns = /* @__PURE__ */ new Map();
|
|
1031
|
+
for (const [i, subtypeMeta] of meta.subTypes.entries()) {
|
|
1032
|
+
const alias = assigner.getLiteralAlias(`${from.alias}_s${i}`);
|
|
1033
|
+
joins.push(ctiJoin(from, subtypeMeta, alias));
|
|
1034
|
+
selects.push(...entitySelectFragments(subtypeMeta, alias));
|
|
1035
|
+
for (const field of Object.values(subtypeMeta.fields)) {
|
|
1036
|
+
if (field.fieldName === "id" || !field.serde || field.kind === "primitive" && field.lazy) continue;
|
|
1037
|
+
for (const column of field.serde.columns) {
|
|
1038
|
+
const aliases = subtypeColumns.get(column.columnName);
|
|
1039
|
+
if (aliases) aliases.push(alias);
|
|
1040
|
+
else subtypeColumns.set(column.columnName, [alias]);
|
|
1041
|
+
}
|
|
1042
|
+
}
|
|
1043
|
+
}
|
|
1044
|
+
selects.push({
|
|
1045
|
+
sql: `${require_keywords.safeKq(from.alias)}.${require_keywords.kq("id")} AS ${require_keywords.kq("id")}`,
|
|
1046
|
+
bindings: [],
|
|
1047
|
+
refs: [from.alias]
|
|
1048
|
+
});
|
|
1049
|
+
for (const [column, aliases] of subtypeColumns) {
|
|
1050
|
+
if (aliases.length < 2) continue;
|
|
1051
|
+
selects.push({
|
|
1052
|
+
sql: `COALESCE(${aliases.map((alias) => `${require_keywords.safeKq(alias)}.${require_keywords.kq(column)}`).join(", ")}) AS ${require_keywords.kq(column)}`,
|
|
1053
|
+
bindings: [],
|
|
1054
|
+
refs: aliases
|
|
1055
|
+
});
|
|
1056
|
+
}
|
|
1057
|
+
if (meta.subTypes.length > 0) {
|
|
1058
|
+
const subtypeAliases = joins.slice(meta.baseTypes.length).map((join) => join.source.alias);
|
|
1059
|
+
selects.push({
|
|
1060
|
+
sql: `CASE ${subtypeAliases.map((alias) => `WHEN ${require_keywords.safeKq(alias)}.${require_keywords.kq("id")} IS NOT NULL THEN ?`).join(" ")} ELSE '_' END AS ${require_keywords.kq("__class")}`,
|
|
1061
|
+
bindings: meta.subTypes.map((subtype) => subtype.type),
|
|
1062
|
+
refs: subtypeAliases
|
|
1063
|
+
});
|
|
1064
|
+
}
|
|
1065
|
+
return {
|
|
1066
|
+
joins,
|
|
1067
|
+
selects
|
|
1068
|
+
};
|
|
1069
|
+
}
|
|
1070
|
+
/** Adds the discriminator projection and subtype filter required by STI entity hydration. */
|
|
1071
|
+
function stiEntityPlan(q, from) {
|
|
1072
|
+
if (!require_Tables.isTable(q.select) || from.handle !== require_Tables.getTableMgmt(q.select)) return {
|
|
1073
|
+
selects: void 0,
|
|
1074
|
+
conditions: []
|
|
1075
|
+
};
|
|
1076
|
+
const meta = require_Tables.getTableMetadata(q.select);
|
|
1077
|
+
if (meta.inheritanceType !== "sti") return {
|
|
1078
|
+
selects: void 0,
|
|
1079
|
+
conditions: []
|
|
1080
|
+
};
|
|
1081
|
+
const selects = from.entitySelects.map((sql) => ({
|
|
1082
|
+
sql,
|
|
1083
|
+
bindings: [],
|
|
1084
|
+
refs: [from.alias]
|
|
1085
|
+
}));
|
|
1086
|
+
if (!(from.entitySelects.length === 1 && from.entitySelects[0] === require_keywords.kqStar(from.alias))) {
|
|
1087
|
+
const discriminator = require_EntityMetadata.getBaseMeta(meta).stiDiscriminatorColumnName;
|
|
1088
|
+
selects.push({
|
|
1089
|
+
sql: `${require_keywords.safeKq(from.alias)}.${require_keywords.kq(discriminator)} AS ${require_keywords.kq(discriminator)}`,
|
|
1090
|
+
bindings: [],
|
|
1091
|
+
refs: [from.alias]
|
|
1092
|
+
});
|
|
1093
|
+
}
|
|
1094
|
+
const condition = require_QueryParser.stiSubtypeFilter(meta, from.alias);
|
|
1095
|
+
return {
|
|
1096
|
+
selects,
|
|
1097
|
+
conditions: condition ? [condition] : []
|
|
1098
|
+
};
|
|
1099
|
+
}
|
|
1100
|
+
/** Creates one mandatory CTI join through the selected source's primary key. */
|
|
1101
|
+
function ctiJoin(from, meta, alias) {
|
|
1102
|
+
const on = {
|
|
1103
|
+
sql: `${require_keywords.safeKq(from.alias)}.${require_keywords.kq("id")} = ${require_keywords.safeKq(alias)}.${require_keywords.kq("id")}`,
|
|
1104
|
+
bindings: [],
|
|
1105
|
+
refs: [from.alias, alias]
|
|
1106
|
+
};
|
|
1107
|
+
return {
|
|
1108
|
+
kind: "left",
|
|
1109
|
+
source: {
|
|
1110
|
+
handle: {
|
|
1111
|
+
tableName: meta.tableName,
|
|
1112
|
+
meta
|
|
1113
|
+
},
|
|
1114
|
+
alias,
|
|
1115
|
+
sql: `${require_keywords.kq(meta.tableName)} AS ${require_keywords.safeKq(alias)}`,
|
|
1116
|
+
bindings: [],
|
|
1117
|
+
refs: [],
|
|
1118
|
+
entitySelects: [],
|
|
1119
|
+
meta
|
|
1120
|
+
},
|
|
1121
|
+
userOn: on,
|
|
1122
|
+
fullOn: on,
|
|
1123
|
+
keep: true
|
|
1124
|
+
};
|
|
1125
|
+
}
|
|
1126
|
+
/** Selects one physical CTI table while preserving lazy-field exclusions. */
|
|
1127
|
+
function entitySelectFragments(meta, alias) {
|
|
1128
|
+
return (meta.hasLazyColumns ? require_QueryParser.lazyExcludedSelects(meta, alias) : [require_keywords.kqStar(alias)]).map((sql) => ({
|
|
1129
|
+
sql,
|
|
1130
|
+
bindings: [],
|
|
1131
|
+
refs: [alias]
|
|
1132
|
+
}));
|
|
1133
|
+
}
|
|
1003
1134
|
function decodeRow(row, decoders) {
|
|
1004
1135
|
const result = {};
|
|
1005
1136
|
for (const [key, expr] of decoders) {
|