neoorm 0.3.0 → 0.4.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (189) hide show
  1. package/README.md +12 -6
  2. package/dist/bin/neoorm.js +45 -15
  3. package/dist/bin/neoorm.js.map +1 -1
  4. package/dist/codegen/diff-manifest.d.ts +7 -7
  5. package/dist/codegen/diff-manifest.d.ts.map +1 -1
  6. package/dist/codegen/diff-manifest.js +67 -54
  7. package/dist/codegen/diff-manifest.js.map +1 -1
  8. package/dist/codegen/generate-summary.js.map +1 -1
  9. package/dist/codegen/generate.d.ts.map +1 -1
  10. package/dist/codegen/generate.js +11 -1
  11. package/dist/codegen/generate.js.map +1 -1
  12. package/dist/codegen/manifest-relations.d.ts.map +1 -1
  13. package/dist/codegen/manifest-relations.js +21 -0
  14. package/dist/codegen/manifest-relations.js.map +1 -1
  15. package/dist/codegen/schema-to-manifest.js.map +1 -1
  16. package/dist/config.d.ts +1 -1
  17. package/dist/config.d.ts.map +1 -1
  18. package/dist/config.js +1 -1
  19. package/dist/config.js.map +1 -1
  20. package/dist/dialect/postgres.d.ts +3 -4
  21. package/dist/dialect/postgres.d.ts.map +1 -1
  22. package/dist/dialect/postgres.js +19 -11
  23. package/dist/dialect/postgres.js.map +1 -1
  24. package/dist/dialect/shared.d.ts +6 -0
  25. package/dist/dialect/shared.d.ts.map +1 -0
  26. package/dist/dialect/shared.js +16 -0
  27. package/dist/dialect/shared.js.map +1 -0
  28. package/dist/dialect/sqlite.d.ts +4 -0
  29. package/dist/dialect/sqlite.d.ts.map +1 -0
  30. package/dist/dialect/sqlite.js +276 -0
  31. package/dist/dialect/sqlite.js.map +1 -0
  32. package/dist/dialect/types.d.ts +7 -0
  33. package/dist/dialect/types.d.ts.map +1 -1
  34. package/dist/init/scaffold.js.map +1 -1
  35. package/dist/introspect/pull.d.ts +3 -2
  36. package/dist/introspect/pull.d.ts.map +1 -1
  37. package/dist/introspect/pull.js +124 -13
  38. package/dist/introspect/pull.js.map +1 -1
  39. package/dist/introspect/queries.d.ts +9 -9
  40. package/dist/introspect/queries.d.ts.map +1 -1
  41. package/dist/introspect/queries.js +16 -16
  42. package/dist/introspect/queries.js.map +1 -1
  43. package/dist/introspect/sqlite/to-manifest.d.ts +4 -0
  44. package/dist/introspect/sqlite/to-manifest.d.ts.map +1 -0
  45. package/dist/introspect/sqlite/to-manifest.js +166 -0
  46. package/dist/introspect/sqlite/to-manifest.js.map +1 -0
  47. package/dist/introspect/to-manifest.d.ts +2 -2
  48. package/dist/introspect/to-manifest.d.ts.map +1 -1
  49. package/dist/introspect/to-manifest.js +20 -12
  50. package/dist/introspect/to-manifest.js.map +1 -1
  51. package/dist/migrate/runner.d.ts +14 -14
  52. package/dist/migrate/runner.d.ts.map +1 -1
  53. package/dist/migrate/runner.js +83 -88
  54. package/dist/migrate/runner.js.map +1 -1
  55. package/dist/plugins/builtin.d.ts +1 -0
  56. package/dist/plugins/builtin.d.ts.map +1 -1
  57. package/dist/plugins/builtin.js +96 -6
  58. package/dist/plugins/builtin.js.map +1 -1
  59. package/dist/plugins/json/operators.d.ts.map +1 -1
  60. package/dist/plugins/json/operators.js +14 -5
  61. package/dist/plugins/json/operators.js.map +1 -1
  62. package/dist/plugins/types.d.ts +4 -4
  63. package/dist/plugins/types.d.ts.map +1 -1
  64. package/dist/runtime/client.d.ts +32 -7
  65. package/dist/runtime/client.d.ts.map +1 -1
  66. package/dist/runtime/client.js +68 -9
  67. package/dist/runtime/client.js.map +1 -1
  68. package/dist/runtime/driver.d.ts +28 -0
  69. package/dist/runtime/driver.d.ts.map +1 -0
  70. package/dist/runtime/driver.js +265 -0
  71. package/dist/runtime/driver.js.map +1 -0
  72. package/dist/runtime/errors.d.ts +1 -1
  73. package/dist/runtime/errors.d.ts.map +1 -1
  74. package/dist/runtime/errors.js +1 -0
  75. package/dist/runtime/errors.js.map +1 -1
  76. package/dist/runtime/executor.d.ts +11 -3
  77. package/dist/runtime/executor.d.ts.map +1 -1
  78. package/dist/runtime/executor.js +69 -37
  79. package/dist/runtime/executor.js.map +1 -1
  80. package/dist/runtime/pg-error.d.ts +1 -1
  81. package/dist/runtime/pg-error.d.ts.map +1 -1
  82. package/dist/runtime/pg-error.js +6 -1
  83. package/dist/runtime/pg-error.js.map +1 -1
  84. package/dist/runtime/query/aggregate.d.ts.map +1 -1
  85. package/dist/runtime/query/aggregate.js +5 -4
  86. package/dist/runtime/query/aggregate.js.map +1 -1
  87. package/dist/runtime/query/compile.d.ts +42 -16
  88. package/dist/runtime/query/compile.d.ts.map +1 -1
  89. package/dist/runtime/query/compile.js +479 -91
  90. package/dist/runtime/query/compile.js.map +1 -1
  91. package/dist/runtime/query/count.d.ts.map +1 -1
  92. package/dist/runtime/query/count.js +7 -3
  93. package/dist/runtime/query/count.js.map +1 -1
  94. package/dist/runtime/query/create.d.ts +5 -0
  95. package/dist/runtime/query/create.d.ts.map +1 -1
  96. package/dist/runtime/query/create.js +89 -23
  97. package/dist/runtime/query/create.js.map +1 -1
  98. package/dist/runtime/query/cursor.d.ts +4 -3
  99. package/dist/runtime/query/cursor.d.ts.map +1 -1
  100. package/dist/runtime/query/cursor.js +13 -9
  101. package/dist/runtime/query/cursor.js.map +1 -1
  102. package/dist/runtime/query/delete.d.ts +2 -1
  103. package/dist/runtime/query/delete.d.ts.map +1 -1
  104. package/dist/runtime/query/delete.js +27 -14
  105. package/dist/runtime/query/delete.js.map +1 -1
  106. package/dist/runtime/query/execute.d.ts +11 -4
  107. package/dist/runtime/query/execute.d.ts.map +1 -1
  108. package/dist/runtime/query/execute.js +20 -5
  109. package/dist/runtime/query/execute.js.map +1 -1
  110. package/dist/runtime/query/find-or-create.d.ts +17 -0
  111. package/dist/runtime/query/find-or-create.d.ts.map +1 -0
  112. package/dist/runtime/query/find-or-create.js +90 -0
  113. package/dist/runtime/query/find-or-create.js.map +1 -0
  114. package/dist/runtime/query/find.d.ts +13 -4
  115. package/dist/runtime/query/find.d.ts.map +1 -1
  116. package/dist/runtime/query/find.js +201 -47
  117. package/dist/runtime/query/find.js.map +1 -1
  118. package/dist/runtime/query/manifest-lookup.d.ts +3 -2
  119. package/dist/runtime/query/manifest-lookup.d.ts.map +1 -1
  120. package/dist/runtime/query/manifest-lookup.js +5 -4
  121. package/dist/runtime/query/manifest-lookup.js.map +1 -1
  122. package/dist/runtime/query/paginate.d.ts.map +1 -1
  123. package/dist/runtime/query/paginate.js +16 -11
  124. package/dist/runtime/query/paginate.js.map +1 -1
  125. package/dist/runtime/query/primary-key.d.ts +9 -6
  126. package/dist/runtime/query/primary-key.d.ts.map +1 -1
  127. package/dist/runtime/query/primary-key.js +65 -35
  128. package/dist/runtime/query/primary-key.js.map +1 -1
  129. package/dist/runtime/query/relation-planner.d.ts +74 -0
  130. package/dist/runtime/query/relation-planner.d.ts.map +1 -0
  131. package/dist/runtime/query/relation-planner.js +648 -0
  132. package/dist/runtime/query/relation-planner.js.map +1 -0
  133. package/dist/runtime/query/relation-writes.d.ts +2 -1
  134. package/dist/runtime/query/relation-writes.d.ts.map +1 -1
  135. package/dist/runtime/query/relation-writes.js +46 -53
  136. package/dist/runtime/query/relation-writes.js.map +1 -1
  137. package/dist/runtime/query/table-index.d.ts +61 -0
  138. package/dist/runtime/query/table-index.d.ts.map +1 -0
  139. package/dist/runtime/query/table-index.js +152 -0
  140. package/dist/runtime/query/table-index.js.map +1 -0
  141. package/dist/runtime/query/unique.d.ts +3 -2
  142. package/dist/runtime/query/unique.d.ts.map +1 -1
  143. package/dist/runtime/query/unique.js +7 -6
  144. package/dist/runtime/query/unique.js.map +1 -1
  145. package/dist/runtime/query/update.d.ts +3 -1
  146. package/dist/runtime/query/update.d.ts.map +1 -1
  147. package/dist/runtime/query/update.js +117 -36
  148. package/dist/runtime/query/update.js.map +1 -1
  149. package/dist/runtime/query/updated-at.d.ts +6 -4
  150. package/dist/runtime/query/updated-at.d.ts.map +1 -1
  151. package/dist/runtime/query/updated-at.js +23 -9
  152. package/dist/runtime/query/updated-at.js.map +1 -1
  153. package/dist/runtime/query/upsert.d.ts.map +1 -1
  154. package/dist/runtime/query/upsert.js +11 -9
  155. package/dist/runtime/query/upsert.js.map +1 -1
  156. package/dist/runtime/sqlite-open.d.ts +3 -0
  157. package/dist/runtime/sqlite-open.d.ts.map +1 -0
  158. package/dist/runtime/sqlite-open.js +22 -0
  159. package/dist/runtime/sqlite-open.js.map +1 -0
  160. package/dist/runtime/transaction.d.ts +5 -0
  161. package/dist/runtime/transaction.d.ts.map +1 -0
  162. package/dist/runtime/transaction.js +26 -0
  163. package/dist/runtime/transaction.js.map +1 -0
  164. package/dist/runtime/types.d.ts +9 -4
  165. package/dist/runtime/types.d.ts.map +1 -1
  166. package/dist/schema/column.d.ts +1 -1
  167. package/dist/schema/column.d.ts.map +1 -1
  168. package/dist/schema/column.js +1 -1
  169. package/dist/schema/column.js.map +1 -1
  170. package/dist/schema/define-schema.js.map +1 -1
  171. package/dist/schema/index.d.ts +2 -2
  172. package/dist/schema/index.d.ts.map +1 -1
  173. package/dist/schema/index.js +1 -1
  174. package/dist/schema/index.js.map +1 -1
  175. package/dist/schema/relation-types.d.ts +1 -0
  176. package/dist/schema/relation-types.d.ts.map +1 -1
  177. package/dist/schema/types.d.ts +12 -0
  178. package/dist/schema/types.d.ts.map +1 -1
  179. package/dist/sql/builder.d.ts.map +1 -1
  180. package/dist/sql/template.d.ts +7 -0
  181. package/dist/sql/template.d.ts.map +1 -1
  182. package/dist/sql/template.js +89 -3
  183. package/dist/sql/template.js.map +1 -1
  184. package/dist/utils/case.d.ts +4 -0
  185. package/dist/utils/case.d.ts.map +1 -1
  186. package/dist/utils/case.js +11 -0
  187. package/dist/utils/case.js.map +1 -1
  188. package/dist/utils/uuid.js.map +1 -1
  189. package/package.json +21 -4
@@ -0,0 +1,648 @@
1
+ import { quoteIdentifier, tableRef, } from "../../dialect/postgres.js";
2
+ import { buildQualifiedSelectColumns, buildSelectColumns, compileOrderBy, compileWhere, mapRowToTs, normalizeLimitOffset, normalizeSelectColumns, orderByShapeKey, } from "./compile.js";
3
+ import { findM2M, findRelation, tableOwnsFkColumn, } from "./manifest-lookup.js";
4
+ import { requireScalarPrimaryKey, targetRelationPkSql, } from "./primary-key.js";
5
+ import { columnsByTsNames, getOrSetSqlCache, getTableIndex, } from "./table-index.js";
6
+ function relationPlanCacheKey(withSpec, dialect, options) {
7
+ const useHasManyAggregate = options?.useHasManyAggregate !== false;
8
+ return `${dialect.name}|${withShapeSignature(withSpec)}|${useHasManyAggregate ? "agg" : "corr"}`;
9
+ }
10
+ export function inlineRelationColumnAlias(relationName) {
11
+ return `__neoorm_${relationName}`;
12
+ }
13
+ export function inlineCountColumnAlias(relationName) {
14
+ return `__neoorm_count_${relationName}`;
15
+ }
16
+ function splitWithSpec(withSpec) {
17
+ const relationWith = {};
18
+ let countSpec;
19
+ for (const [key, value] of Object.entries(withSpec)) {
20
+ if (key === "_count") {
21
+ countSpec = value;
22
+ continue;
23
+ }
24
+ relationWith[key] = value;
25
+ }
26
+ const result = { relationWith };
27
+ if (countSpec)
28
+ result.countSpec = countSpec;
29
+ return result;
30
+ }
31
+ function toInlineSpec(withInput) {
32
+ return typeof withInput === "object" ? withInput : undefined;
33
+ }
34
+ function parentPkRef(parentTable) {
35
+ const { sqlName } = requireScalarPrimaryKey(parentTable);
36
+ return `${tableRef(parentTable)}.${quoteIdentifier(sqlName)}`;
37
+ }
38
+ function parentPkRefForAlias(parentTable, parentAlias) {
39
+ const { sqlName } = requireScalarPrimaryKey(parentTable);
40
+ return `${quoteIdentifier(parentAlias)}.${quoteIdentifier(sqlName)}`;
41
+ }
42
+ function columnsForInlineSelect(table, nestedSpec, manifestIndex) {
43
+ const selectKeys = normalizeSelectColumns(nestedSpec?.select);
44
+ if (!selectKeys || selectKeys.length === 0)
45
+ return table.columns;
46
+ return columnsByTsNames(getTableIndex(manifestIndex, table.accessor), table, selectKeys);
47
+ }
48
+ function tryBuildInlineChainNode(manifest, parentTable, relationName, withInput) {
49
+ if (findM2M(manifest, parentTable.accessor, relationName))
50
+ return null;
51
+ const relation = findRelation(parentTable, relationName);
52
+ if (!relation)
53
+ return null;
54
+ const targetTable = manifest.tables[relation.targetAccessor];
55
+ if (!targetTable)
56
+ return null;
57
+ const nestedSpec = toInlineSpec(withInput);
58
+ const nestedWith = nestedSpec?.with;
59
+ if (relation.cardinality === "many") {
60
+ if (tableOwnsFkColumn(parentTable, relation))
61
+ return null;
62
+ if (nestedWith && Object.keys(nestedWith).length > 0) {
63
+ const nestedEntries = Object.entries(nestedWith);
64
+ if (nestedEntries.length !== 1)
65
+ return null;
66
+ const [childName, childInput] = nestedEntries[0];
67
+ const childChain = tryBuildInlineChainNode(manifest, targetTable, childName, childInput);
68
+ if (!childChain)
69
+ return null;
70
+ const node = {
71
+ relationName,
72
+ relation,
73
+ targetTable,
74
+ child: childChain,
75
+ };
76
+ if (nestedSpec)
77
+ node.nestedSpec = nestedSpec;
78
+ return node;
79
+ }
80
+ const node = {
81
+ relationName,
82
+ relation,
83
+ targetTable,
84
+ };
85
+ if (nestedSpec)
86
+ node.nestedSpec = nestedSpec;
87
+ return node;
88
+ }
89
+ if (relation.cardinality === "one" &&
90
+ tableOwnsFkColumn(parentTable, relation)) {
91
+ if (nestedWith && Object.keys(nestedWith).length > 0)
92
+ return null;
93
+ const node = {
94
+ relationName,
95
+ relation,
96
+ targetTable,
97
+ };
98
+ if (nestedSpec)
99
+ node.nestedSpec = nestedSpec;
100
+ return node;
101
+ }
102
+ return null;
103
+ }
104
+ function canInlineCount(manifest, parentTable, relationName) {
105
+ if (findM2M(manifest, parentTable.accessor, relationName))
106
+ return false;
107
+ const relation = findRelation(parentTable, relationName);
108
+ return relation?.cardinality === "many";
109
+ }
110
+ function isSimpleCountSpec(spec) {
111
+ return (spec === true ||
112
+ (typeof spec === "object" && spec.where === undefined));
113
+ }
114
+ function tryBuildCountAggregatePlan(manifest, parentTable, countSpec, dialect, manifestIndex) {
115
+ for (const spec of Object.values(countSpec)) {
116
+ if (!isSimpleCountSpec(spec))
117
+ return undefined;
118
+ }
119
+ const parentRef = tableRef(parentTable);
120
+ const parentTableIndex = getTableIndex(manifestIndex, parentTable.accessor);
121
+ const groupByCols = parentTable.columns.map((col) => `${parentRef}.${quoteIdentifier(col.sqlName)}`);
122
+ const joins = [];
123
+ const selectCols = [];
124
+ const inlineCounts = [];
125
+ for (const [relationName, spec] of Object.entries(countSpec)) {
126
+ if (!canInlineCount(manifest, parentTable, relationName)) {
127
+ return undefined;
128
+ }
129
+ const relation = findRelation(parentTable, relationName, parentTableIndex);
130
+ if (!relation)
131
+ return undefined;
132
+ const targetTable = manifest.tables[relation.targetAccessor];
133
+ if (!targetTable)
134
+ return undefined;
135
+ const targetAlias = `_cnt_${relationName}`;
136
+ const fkCol = quoteIdentifier(relation.fkSqlColumn);
137
+ const parentPkCol = quoteIdentifier(requireScalarPrimaryKey(parentTable).sqlName);
138
+ const targetPkCol = quoteIdentifier(targetRelationPkSql(targetTable, relation));
139
+ joins.push(`LEFT JOIN ${tableRef(targetTable)} AS ${quoteIdentifier(targetAlias)} ON ${quoteIdentifier(targetAlias)}.${fkCol} = ${parentRef}.${parentPkCol}`);
140
+ selectCols.push(`${dialect.castToInt(`COUNT(${quoteIdentifier(targetAlias)}.${targetPkCol})`)} AS ${quoteIdentifier(inlineCountColumnAlias(relationName))}`);
141
+ inlineCounts.push({ relationName, spec });
142
+ }
143
+ return { joins, selectCols, groupByCols, inlineCounts };
144
+ }
145
+ function canHasManyAggregate(manifest, parentTable, chain) {
146
+ if (findM2M(manifest, parentTable.accessor, chain.relationName)) {
147
+ return false;
148
+ }
149
+ if (chain.child)
150
+ return false;
151
+ if (tableOwnsFkColumn(parentTable, chain.relation))
152
+ return false;
153
+ const spec = chain.nestedSpec;
154
+ if (spec?.with && Object.keys(spec.with).length > 0)
155
+ return false;
156
+ if (spec?.orderBy)
157
+ return false;
158
+ if (spec?.limit !== undefined)
159
+ return false;
160
+ return true;
161
+ }
162
+ function groupByExpressionsFromJoinSelectCols(selectCols) {
163
+ return selectCols.map((col) => {
164
+ const asIdx = col.search(/\s+AS\s+/i);
165
+ return asIdx >= 0 ? col.slice(0, asIdx).trim() : col;
166
+ });
167
+ }
168
+ function tryBuildHasManyAggregatePlan(manifest, parentTable, chains, toOneJoins, dialect, manifestIndex) {
169
+ if (chains.length === 0)
170
+ return undefined;
171
+ const parentRef = tableRef(parentTable);
172
+ const parentTableIndex = getTableIndex(manifestIndex, parentTable.accessor);
173
+ const parentPkCol = quoteIdentifier(requireScalarPrimaryKey(parentTable).sqlName);
174
+ const groupByCols = parentTable.columns.map((col) => `${parentRef}.${quoteIdentifier(col.sqlName)}`);
175
+ groupByCols.push(...groupByExpressionsFromJoinSelectCols(toOneJoins.selectCols));
176
+ const joins = [...toOneJoins.joins];
177
+ const selectCols = [];
178
+ for (const { relationName, chain } of chains) {
179
+ const relation = findRelation(parentTable, relationName, parentTableIndex);
180
+ if (!relation)
181
+ return undefined;
182
+ const targetTable = manifest.tables[relation.targetAccessor];
183
+ if (!targetTable)
184
+ return undefined;
185
+ const targetAlias = `_hm_${relationName}`;
186
+ const fkCol = quoteIdentifier(relation.fkSqlColumn);
187
+ const targetPkCol = quoteIdentifier(targetRelationPkSql(targetTable, relation));
188
+ joins.push(`LEFT JOIN ${tableRef(targetTable)} AS ${quoteIdentifier(targetAlias)} ON ${quoteIdentifier(targetAlias)}.${fkCol} = ${parentRef}.${parentPkCol}`);
189
+ const rowExpr = buildHasManyRowExpression(chain, targetAlias, dialect, manifestIndex);
190
+ selectCols.push(`COALESCE(${dialect.jsonAggExpr(rowExpr)} FILTER (WHERE ${quoteIdentifier(targetAlias)}.${targetPkCol} IS NOT NULL), '[]') AS ${quoteIdentifier(inlineRelationColumnAlias(relationName))}`);
191
+ }
192
+ return { joins, selectCols, groupByCols, inlineJsonAgg: chains };
193
+ }
194
+ function buildJoinClauses(manifest, parentTable, withSpec, manifestIndex) {
195
+ const joins = [];
196
+ const selectCols = [];
197
+ const joinedRelations = new Set();
198
+ const parentTableIndex = getTableIndex(manifestIndex, parentTable.accessor);
199
+ for (const [relationName, spec] of Object.entries(withSpec)) {
200
+ const relation = findRelation(parentTable, relationName, parentTableIndex);
201
+ if (!relation)
202
+ continue;
203
+ if (relation.cardinality !== "one")
204
+ continue;
205
+ if (!tableOwnsFkColumn(parentTable, relation, parentTableIndex))
206
+ continue;
207
+ if (findM2M(manifest, parentTable.accessor, relationName))
208
+ continue;
209
+ const nestedSpec = toInlineSpec(spec);
210
+ if (nestedSpec?.with)
211
+ continue;
212
+ const targetTable = manifest.tables[relation.targetAccessor];
213
+ if (!targetTable)
214
+ continue;
215
+ const alias = `__${relationName}`;
216
+ const parentFkCol = quoteIdentifier(relation.fkSqlColumn);
217
+ const targetPkCol = quoteIdentifier(targetRelationPkSql(targetTable, relation));
218
+ const onClause = `${quoteIdentifier(alias)}.${targetPkCol} = ${tableRef(parentTable)}.${parentFkCol}`;
219
+ joins.push(`LEFT JOIN ${tableRef(targetTable)} AS ${quoteIdentifier(alias)} ON ${onClause}`);
220
+ const targetTableIndex = getTableIndex(manifestIndex, targetTable.accessor);
221
+ const selectKeys = nestedSpec?.select
222
+ ? normalizeSelectColumns(nestedSpec.select)
223
+ : undefined;
224
+ const targetCols = selectKeys && selectKeys.length > 0
225
+ ? columnsByTsNames(targetTableIndex, targetTable, selectKeys)
226
+ : targetTable.columns;
227
+ for (const col of targetCols) {
228
+ const prefixedName = `__${relationName}__${col.sqlName}`;
229
+ selectCols.push(`${quoteIdentifier(alias)}.${quoteIdentifier(col.sqlName)} AS ${quoteIdentifier(prefixedName)}`);
230
+ }
231
+ joinedRelations.add(relationName);
232
+ }
233
+ return { joins, selectCols, joinedRelations };
234
+ }
235
+ function buildToOneScalarSubquery(node, parentRowAlias, dialect, manifestIndex) {
236
+ const { relation, targetTable } = node;
237
+ const targetAlias = `${parentRowAlias}_rel`;
238
+ const targetPkCol = quoteIdentifier(targetRelationPkSql(targetTable, relation));
239
+ const parentFkCol = quoteIdentifier(relation.fkSqlColumn);
240
+ const cols = columnsForInlineSelect(targetTable, node.nestedSpec, manifestIndex);
241
+ const refs = cols.map((col) => `${quoteIdentifier(targetAlias)}.${quoteIdentifier(col.sqlName)}`);
242
+ const selectList = refs.join(", ");
243
+ const outerRefs = cols.map((col) => `sub.${quoteIdentifier(col.sqlName)}`);
244
+ return `(SELECT ${dialect.rowToJsonObject(cols, outerRefs, "sub")} FROM (SELECT ${selectList} FROM ${tableRef(targetTable)} ${quoteIdentifier(targetAlias)} WHERE ${quoteIdentifier(targetAlias)}.${targetPkCol} = ${quoteIdentifier(parentRowAlias)}.${parentFkCol} LIMIT 1) sub)`;
245
+ }
246
+ function buildHasManyRowExpression(node, rowAlias, dialect, manifestIndex) {
247
+ if (node.child) {
248
+ const cols = columnsForInlineSelect(node.targetTable, node.nestedSpec, manifestIndex);
249
+ const entries = cols.map((col) => `'${col.sqlName}', ${quoteIdentifier(rowAlias)}.${quoteIdentifier(col.sqlName)}`);
250
+ const nested = buildChildAggregationExpr(node.child, rowAlias, node.targetTable, dialect, manifestIndex);
251
+ entries.push(`'${node.child.relationName}', ${nested}`);
252
+ return dialect.jsonBuildObjectExpr(entries);
253
+ }
254
+ const cols = columnsForInlineSelect(node.targetTable, undefined, manifestIndex);
255
+ const refs = cols.map((col) => `${quoteIdentifier(rowAlias)}.${quoteIdentifier(col.sqlName)}`);
256
+ return dialect.rowToJsonObject(cols, refs, `${quoteIdentifier(rowAlias)}.*`);
257
+ }
258
+ function buildHasManySubqueryFromRef(node, parentTable, parentCorrelationRef, dialect, manifestIndex) {
259
+ const childAlias = `_r_${node.relationName}`;
260
+ const fkCol = quoteIdentifier(node.relation.fkSqlColumn);
261
+ const rowExpr = buildHasManyRowExpression(node, childAlias, dialect, manifestIndex);
262
+ let sql = `(SELECT ${dialect.jsonAggExpr("agg_row")} FROM (SELECT ${rowExpr} AS agg_row FROM ${tableRef(node.targetTable)} ${quoteIdentifier(childAlias)} WHERE ${quoteIdentifier(childAlias)}.${fkCol} = ${parentCorrelationRef}`;
263
+ if (node.nestedSpec?.orderBy) {
264
+ sql += ` ${compileOrderBy(node.targetTable, node.nestedSpec.orderBy, childAlias, manifestIndex)}`;
265
+ }
266
+ if (node.nestedSpec?.limit !== undefined) {
267
+ sql += ` LIMIT ${normalizeLimitOffset(node.nestedSpec.limit, "limit")}`;
268
+ }
269
+ sql += ") agg)";
270
+ return sql;
271
+ }
272
+ function buildChildAggregationExpr(node, parentRowAlias, parentTable, dialect, manifestIndex) {
273
+ const parentTableIndex = getTableIndex(manifestIndex, parentTable.accessor);
274
+ if (node.relation.cardinality === "one" &&
275
+ tableOwnsFkColumn(parentTable, node.relation, parentTableIndex)) {
276
+ return buildToOneScalarSubquery(node, parentRowAlias, dialect, manifestIndex);
277
+ }
278
+ if (node.relation.cardinality === "many" &&
279
+ !tableOwnsFkColumn(parentTable, node.relation, parentTableIndex)) {
280
+ const parentRef = parentPkRefForAlias(parentTable, parentRowAlias);
281
+ return buildHasManySubqueryFromRef(node, parentTable, parentRef, dialect, manifestIndex);
282
+ }
283
+ throw new Error(`Unsupported inline relation chain node: ${node.relationName}`);
284
+ }
285
+ export function buildInlineJsonAggSelectCol(parentTable, chain, dialect, manifestIndex) {
286
+ const parentRef = parentPkRef(parentTable);
287
+ const subquery = buildHasManySubqueryFromRef(chain, parentTable, parentRef, dialect, manifestIndex);
288
+ const alias = quoteIdentifier(inlineRelationColumnAlias(chain.relationName));
289
+ return `${subquery} AS ${alias}`;
290
+ }
291
+ export function buildInlineCountSelectCol(manifest, parentTable, relationName, spec, dialect, manifestIndex) {
292
+ const parentTableIndex = getTableIndex(manifestIndex, parentTable.accessor);
293
+ const relation = findRelation(parentTable, relationName, parentTableIndex);
294
+ if (!relation || relation.cardinality !== "many") {
295
+ throw new Error(`Cannot inline count for relation: ${relationName}`);
296
+ }
297
+ const targetTable = manifest.tables[relation.targetAccessor];
298
+ if (!targetTable) {
299
+ throw new Error(`Unknown target table for relation: ${relationName}`);
300
+ }
301
+ const parentRef = parentPkRef(parentTable);
302
+ const targetAlias = `_cnt_${relationName}`;
303
+ const fkCol = quoteIdentifier(relation.fkSqlColumn);
304
+ const whereFilter = typeof spec === "object" ? spec.where : undefined;
305
+ let extraWhere = "";
306
+ if (whereFilter) {
307
+ const compiled = compileWhere(manifest, targetTable, whereFilter, dialect, 1, manifestIndex);
308
+ if (compiled.sql) {
309
+ const adjusted = compiled.sql.replace(/^WHERE\s+/i, "");
310
+ extraWhere = ` AND ${adjusted}`;
311
+ }
312
+ }
313
+ const subquery = `(SELECT ${dialect.castToInt("COUNT(*)")} FROM ${tableRef(targetTable)} ${quoteIdentifier(targetAlias)} WHERE ${quoteIdentifier(targetAlias)}.${fkCol} = ${parentRef}${extraWhere})`;
314
+ const alias = quoteIdentifier(inlineCountColumnAlias(relationName));
315
+ return `${subquery} AS ${alias}`;
316
+ }
317
+ export function planRelationLoad(manifest, parentTable, withSpec, dialect, manifestIndex, options) {
318
+ const emptyPlan = {
319
+ joins: [],
320
+ joinSelectCols: [],
321
+ joinedRelations: new Set(),
322
+ inlineJsonAgg: [],
323
+ inlineCounts: [],
324
+ batchWith: {},
325
+ };
326
+ if (!withSpec)
327
+ return emptyPlan;
328
+ const parentTableIndex = getTableIndex(manifestIndex, parentTable.accessor);
329
+ const { relationWith, countSpec } = splitWithSpec(withSpec);
330
+ const joinCandidates = {};
331
+ const batchWith = {};
332
+ const pendingHasMany = [];
333
+ const inlineJsonAgg = [];
334
+ const inlineCounts = [];
335
+ const batchCounts = {};
336
+ for (const [relationName, withInput] of Object.entries(relationWith)) {
337
+ const chain = tryBuildInlineChainNode(manifest, parentTable, relationName, withInput);
338
+ if (chain &&
339
+ chain.relation.cardinality === "many" &&
340
+ !tableOwnsFkColumn(parentTable, chain.relation, parentTableIndex)) {
341
+ pendingHasMany.push({ relationName, chain });
342
+ continue;
343
+ }
344
+ const relation = findRelation(parentTable, relationName, parentTableIndex);
345
+ if (relation?.cardinality === "one" &&
346
+ tableOwnsFkColumn(parentTable, relation, parentTableIndex) &&
347
+ !findM2M(manifest, parentTable.accessor, relationName) &&
348
+ !toInlineSpec(withInput)?.with) {
349
+ joinCandidates[relationName] = withInput;
350
+ continue;
351
+ }
352
+ batchWith[relationName] = withInput;
353
+ }
354
+ const aggregateChains = [];
355
+ for (const item of pendingHasMany) {
356
+ if (canHasManyAggregate(manifest, parentTable, item.chain)) {
357
+ aggregateChains.push(item);
358
+ }
359
+ else {
360
+ inlineJsonAgg.push(item);
361
+ }
362
+ }
363
+ const { joins, selectCols, joinedRelations } = buildJoinClauses(manifest, parentTable, joinCandidates, manifestIndex);
364
+ let hasManyAggregate;
365
+ const useHasManyAggregate = options?.useHasManyAggregate !== false;
366
+ if (useHasManyAggregate &&
367
+ aggregateChains.length > 0 &&
368
+ aggregateChains.length === pendingHasMany.length) {
369
+ hasManyAggregate = tryBuildHasManyAggregatePlan(manifest, parentTable, aggregateChains, { joins, selectCols, joinedRelations }, dialect, manifestIndex);
370
+ }
371
+ else {
372
+ inlineJsonAgg.push(...aggregateChains);
373
+ }
374
+ if (countSpec) {
375
+ const canAggregate = Object.keys(relationWith).length === 0 &&
376
+ pendingHasMany.length === 0 &&
377
+ Object.keys(joinCandidates).length === 0;
378
+ if (canAggregate) {
379
+ const aggregatePlan = tryBuildCountAggregatePlan(manifest, parentTable, countSpec, dialect, manifestIndex);
380
+ if (aggregatePlan) {
381
+ return {
382
+ joins: aggregatePlan.joins,
383
+ joinSelectCols: [],
384
+ joinedRelations: new Set(),
385
+ inlineJsonAgg: [],
386
+ inlineCounts: aggregatePlan.inlineCounts,
387
+ batchWith: {},
388
+ countAggregate: aggregatePlan,
389
+ };
390
+ }
391
+ }
392
+ for (const [relationName, spec] of Object.entries(countSpec)) {
393
+ if (canInlineCount(manifest, parentTable, relationName)) {
394
+ inlineCounts.push({ relationName, spec });
395
+ }
396
+ else {
397
+ batchCounts[relationName] = spec;
398
+ }
399
+ }
400
+ }
401
+ if (Object.keys(batchCounts).length > 0) {
402
+ batchWith._count = batchCounts;
403
+ }
404
+ if (hasManyAggregate) {
405
+ return {
406
+ joins: [],
407
+ joinSelectCols: selectCols,
408
+ joinedRelations,
409
+ inlineJsonAgg: hasManyAggregate.inlineJsonAgg,
410
+ inlineCounts,
411
+ batchWith,
412
+ hasManyAggregate,
413
+ };
414
+ }
415
+ return {
416
+ joins,
417
+ joinSelectCols: selectCols,
418
+ joinedRelations,
419
+ inlineJsonAgg,
420
+ inlineCounts,
421
+ batchWith,
422
+ };
423
+ }
424
+ function extractJoinedRelations(row, manifest, parentTable, joinedRelations, manifestIndex) {
425
+ const relations = {};
426
+ for (const relationName of joinedRelations) {
427
+ const relation = findRelation(parentTable, relationName);
428
+ if (!relation)
429
+ continue;
430
+ const targetTable = manifest.tables[relation.targetAccessor];
431
+ if (!targetTable)
432
+ continue;
433
+ const prefix = `__${relationName}__`;
434
+ const prefixedKeys = Object.keys(row).filter((k) => k.startsWith(prefix));
435
+ if (prefixedKeys.length === 0)
436
+ continue;
437
+ const allNull = prefixedKeys.every((k) => row[k] == null);
438
+ if (allNull) {
439
+ relations[relationName] = null;
440
+ }
441
+ else {
442
+ const raw = {};
443
+ for (const key of prefixedKeys) {
444
+ raw[key.slice(prefix.length)] = row[key];
445
+ }
446
+ const targetIndex = getTableIndex(manifestIndex, targetTable.accessor);
447
+ relations[relationName] = mapRowToTs(targetIndex, targetTable, raw);
448
+ }
449
+ }
450
+ return relations;
451
+ }
452
+ function parseJsonValue(value) {
453
+ if (typeof value === "string") {
454
+ return JSON.parse(value);
455
+ }
456
+ return value;
457
+ }
458
+ function hydrateInlineChainValue(runtime, node, value) {
459
+ if (value == null) {
460
+ return node.relation.cardinality === "many" ? [] : null;
461
+ }
462
+ const targetIndex = getTableIndex(runtime.tableIndex, node.targetTable.accessor);
463
+ if (node.relation.cardinality === "many") {
464
+ const parsed = parseJsonValue(value);
465
+ if (!Array.isArray(parsed))
466
+ return [];
467
+ return parsed.map((row) => {
468
+ const childRow = row;
469
+ const mapped = mapRowToTs(targetIndex, node.targetTable, childRow);
470
+ if (node.child) {
471
+ const nestedVal = childRow[node.child.relationName];
472
+ mapped[node.child.relationName] = hydrateInlineChainValue(runtime, node.child, nestedVal);
473
+ }
474
+ return mapped;
475
+ });
476
+ }
477
+ const parsed = parseJsonValue(value);
478
+ if (parsed == null || typeof parsed !== "object" || Array.isArray(parsed)) {
479
+ return null;
480
+ }
481
+ return mapRowToTs(targetIndex, node.targetTable, parsed);
482
+ }
483
+ export function hydrateRowsWithPlan(runtime, parentTable, rawRows, plan) {
484
+ const { manifest } = runtime;
485
+ const tableIndex = getTableIndex(runtime.tableIndex, parentTable.accessor);
486
+ return rawRows.map((rawRow) => {
487
+ const joined = plan.joinedRelations.size > 0
488
+ ? extractJoinedRelations(rawRow, manifest, parentTable, plan.joinedRelations, runtime.tableIndex)
489
+ : {};
490
+ const parent = mapRowToTs(tableIndex, parentTable, rawRow);
491
+ const row = { ...parent, ...joined };
492
+ for (const inline of plan.inlineJsonAgg) {
493
+ const alias = inlineRelationColumnAlias(inline.relationName);
494
+ row[inline.relationName] = hydrateInlineChainValue(runtime, inline.chain, rawRow[alias]);
495
+ }
496
+ if (plan.inlineCounts.length > 0) {
497
+ const countBucket = {};
498
+ for (const countPlan of plan.inlineCounts) {
499
+ const alias = inlineCountColumnAlias(countPlan.relationName);
500
+ const val = rawRow[alias];
501
+ countBucket[countPlan.relationName] =
502
+ typeof val === "number" ? val : Number(val ?? 0);
503
+ }
504
+ row["_count"] = countBucket;
505
+ }
506
+ return row;
507
+ });
508
+ }
509
+ export function buildPlanExtraSelectCols(manifest, parentTable, plan, dialect, manifestIndex) {
510
+ if (plan.countAggregate) {
511
+ return [...plan.countAggregate.selectCols];
512
+ }
513
+ if (plan.hasManyAggregate) {
514
+ return [...plan.joinSelectCols, ...plan.hasManyAggregate.selectCols];
515
+ }
516
+ const cols = [...plan.joinSelectCols];
517
+ for (const inline of plan.inlineJsonAgg) {
518
+ cols.push(buildInlineJsonAggSelectCol(parentTable, inline.chain, dialect, manifestIndex));
519
+ }
520
+ for (const countPlan of plan.inlineCounts) {
521
+ cols.push(buildInlineCountSelectCol(manifest, parentTable, countPlan.relationName, countPlan.spec, dialect, manifestIndex));
522
+ }
523
+ return cols;
524
+ }
525
+ export function buildAggregateGroupBy(plan) {
526
+ if (plan.hasManyAggregate && plan.hasManyAggregate.groupByCols.length > 0) {
527
+ return `GROUP BY ${plan.hasManyAggregate.groupByCols.join(", ")}`;
528
+ }
529
+ if (plan.countAggregate && plan.countAggregate.groupByCols.length > 0) {
530
+ return `GROUP BY ${plan.countAggregate.groupByCols.join(", ")}`;
531
+ }
532
+ return "";
533
+ }
534
+ /** @deprecated Use buildAggregateGroupBy */
535
+ export function buildCountAggregateGroupBy(plan) {
536
+ return buildAggregateGroupBy(plan);
537
+ }
538
+ export function compileCountOrderBy(manifest, parentTable, orderBy, plan, manifestIndex) {
539
+ if (!orderBy || !plan.countAggregate)
540
+ return "";
541
+ const countOrder = orderBy._count;
542
+ if (!countOrder || typeof countOrder !== "object" || Array.isArray(countOrder)) {
543
+ return "";
544
+ }
545
+ const parentTableIndex = getTableIndex(manifestIndex, parentTable.accessor);
546
+ const parts = [];
547
+ for (const [relationName, direction] of Object.entries(countOrder)) {
548
+ if (typeof direction !== "string")
549
+ continue;
550
+ const countPlan = plan.countAggregate.inlineCounts.find((c) => c.relationName === relationName);
551
+ if (!countPlan)
552
+ continue;
553
+ const relation = findRelation(parentTable, relationName, parentTableIndex);
554
+ if (!relation)
555
+ continue;
556
+ const targetTable = manifest.tables[relation.targetAccessor];
557
+ if (!targetTable)
558
+ continue;
559
+ const targetAlias = `_cnt_${relationName}`;
560
+ const targetPkCol = quoteIdentifier(targetRelationPkSql(targetTable, relation));
561
+ const dir = direction.toUpperCase() === "DESC" ? "DESC" : "ASC";
562
+ parts.push(`COUNT(${quoteIdentifier(targetAlias)}.${targetPkCol}) ${dir}`);
563
+ }
564
+ return parts.length > 0 ? `ORDER BY ${parts.join(", ")}` : "";
565
+ }
566
+ export function hasPlanMainQueryExtras(plan) {
567
+ return (plan.joinedRelations.size > 0 ||
568
+ plan.inlineJsonAgg.length > 0 ||
569
+ plan.inlineCounts.length > 0 ||
570
+ plan.countAggregate !== undefined ||
571
+ plan.hasManyAggregate !== undefined);
572
+ }
573
+ export function withShapeSignature(withSpec) {
574
+ const parts = [];
575
+ const sorted = Object.entries(withSpec).sort(([a], [b]) => a.localeCompare(b));
576
+ for (const [name, input] of sorted) {
577
+ if (input === true) {
578
+ parts.push(name);
579
+ continue;
580
+ }
581
+ if (typeof input === "object" && input !== null) {
582
+ const spec = input;
583
+ const bits = [name];
584
+ if (spec.limit !== undefined)
585
+ bits.push(`l${spec.limit}`);
586
+ if (spec.orderBy)
587
+ bits.push(`o${orderByShapeKey(spec.orderBy)}`);
588
+ if (spec.with) {
589
+ bits.push(`w${withShapeSignature(spec.with)}`);
590
+ }
591
+ parts.push(bits.join(":"));
592
+ }
593
+ }
594
+ return parts.join("|");
595
+ }
596
+ export function planIsFullyInline(plan) {
597
+ return Object.keys(plan.batchWith).length === 0;
598
+ }
599
+ export function getCachedRelationPlan(manifest, table, withSpec, dialect, manifestIndex, options) {
600
+ const tableIndex = getTableIndex(manifestIndex, table.accessor);
601
+ const signature = relationPlanCacheKey(withSpec, dialect, options);
602
+ const cached = tableIndex?.relationPlanBySignature.get(signature);
603
+ if (cached)
604
+ return cached;
605
+ const plan = planRelationLoad(manifest, table, withSpec, dialect, manifestIndex, options);
606
+ tableIndex?.relationPlanBySignature.set(signature, plan);
607
+ return plan;
608
+ }
609
+ export function getCachedFindByIdWithQuery(manifest, table, withSpec, dialect, manifestIndex) {
610
+ const plan = getCachedRelationPlan(manifest, table, withSpec, dialect, manifestIndex, { useHasManyAggregate: false });
611
+ if (!planIsFullyInline(plan))
612
+ return null;
613
+ if (plan.countAggregate)
614
+ return null;
615
+ const tableIndex = getTableIndex(manifestIndex, table.accessor);
616
+ const signature = `${dialect.name}|${withShapeSignature(withSpec)}|corr`;
617
+ const { sqlName } = requireScalarPrimaryKey(table);
618
+ const pkCol = quoteIdentifier(sqlName);
619
+ const build = () => {
620
+ const joinClauses = plan.hasManyAggregate && plan.hasManyAggregate.joins.length > 0
621
+ ? plan.hasManyAggregate.joins
622
+ : plan.joins.length > 0
623
+ ? plan.joins
624
+ : undefined;
625
+ const hasJoins = Boolean(joinClauses && joinClauses.length > 0);
626
+ const selectCols = hasJoins
627
+ ? buildQualifiedSelectColumns(table, undefined, manifestIndex)
628
+ : buildSelectColumns(table, undefined, manifestIndex);
629
+ const extraCols = buildPlanExtraSelectCols(manifest, table, plan, dialect, manifestIndex);
630
+ let sql = `SELECT ${selectCols}`;
631
+ if (extraCols.length > 0)
632
+ sql += `, ${extraCols.join(", ")}`;
633
+ sql += ` FROM ${tableRef(table)}`;
634
+ if (joinClauses)
635
+ sql += ` ${joinClauses.join(" ")}`;
636
+ sql += ` WHERE ${tableRef(table)}.${pkCol} = $1`;
637
+ const groupBySql = buildAggregateGroupBy(plan);
638
+ if (groupBySql)
639
+ sql += ` ${groupBySql}`;
640
+ return sql;
641
+ };
642
+ if (!tableIndex) {
643
+ return { sql: build(), plan };
644
+ }
645
+ const sql = getOrSetSqlCache(tableIndex.findByIdWithSqlBySignature, signature, build);
646
+ return { sql, plan };
647
+ }
648
+ //# sourceMappingURL=relation-planner.js.map