neoorm 0.3.1 → 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 (179) 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 +21 -4
  65. package/dist/runtime/client.d.ts.map +1 -1
  66. package/dist/runtime/client.js +61 -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.map +1 -1
  73. package/dist/runtime/executor.d.ts +6 -3
  74. package/dist/runtime/executor.d.ts.map +1 -1
  75. package/dist/runtime/executor.js +60 -42
  76. package/dist/runtime/executor.js.map +1 -1
  77. package/dist/runtime/query/aggregate.d.ts.map +1 -1
  78. package/dist/runtime/query/aggregate.js +4 -3
  79. package/dist/runtime/query/aggregate.js.map +1 -1
  80. package/dist/runtime/query/compile.d.ts +37 -24
  81. package/dist/runtime/query/compile.d.ts.map +1 -1
  82. package/dist/runtime/query/compile.js +388 -95
  83. package/dist/runtime/query/compile.js.map +1 -1
  84. package/dist/runtime/query/count.d.ts.map +1 -1
  85. package/dist/runtime/query/count.js +7 -3
  86. package/dist/runtime/query/count.js.map +1 -1
  87. package/dist/runtime/query/create.d.ts +5 -0
  88. package/dist/runtime/query/create.d.ts.map +1 -1
  89. package/dist/runtime/query/create.js +63 -23
  90. package/dist/runtime/query/create.js.map +1 -1
  91. package/dist/runtime/query/cursor.d.ts +4 -3
  92. package/dist/runtime/query/cursor.d.ts.map +1 -1
  93. package/dist/runtime/query/cursor.js +10 -8
  94. package/dist/runtime/query/cursor.js.map +1 -1
  95. package/dist/runtime/query/delete.d.ts +1 -0
  96. package/dist/runtime/query/delete.d.ts.map +1 -1
  97. package/dist/runtime/query/delete.js +17 -6
  98. package/dist/runtime/query/delete.js.map +1 -1
  99. package/dist/runtime/query/execute.d.ts +4 -3
  100. package/dist/runtime/query/execute.d.ts.map +1 -1
  101. package/dist/runtime/query/execute.js +5 -4
  102. package/dist/runtime/query/execute.js.map +1 -1
  103. package/dist/runtime/query/find-or-create.d.ts.map +1 -1
  104. package/dist/runtime/query/find-or-create.js +57 -8
  105. package/dist/runtime/query/find-or-create.js.map +1 -1
  106. package/dist/runtime/query/find.d.ts +12 -3
  107. package/dist/runtime/query/find.d.ts.map +1 -1
  108. package/dist/runtime/query/find.js +159 -232
  109. package/dist/runtime/query/find.js.map +1 -1
  110. package/dist/runtime/query/manifest-lookup.d.ts +3 -2
  111. package/dist/runtime/query/manifest-lookup.d.ts.map +1 -1
  112. package/dist/runtime/query/manifest-lookup.js +5 -4
  113. package/dist/runtime/query/manifest-lookup.js.map +1 -1
  114. package/dist/runtime/query/paginate.d.ts.map +1 -1
  115. package/dist/runtime/query/paginate.js +16 -11
  116. package/dist/runtime/query/paginate.js.map +1 -1
  117. package/dist/runtime/query/primary-key.d.ts +8 -6
  118. package/dist/runtime/query/primary-key.d.ts.map +1 -1
  119. package/dist/runtime/query/primary-key.js +45 -17
  120. package/dist/runtime/query/primary-key.js.map +1 -1
  121. package/dist/runtime/query/relation-planner.d.ts +74 -0
  122. package/dist/runtime/query/relation-planner.d.ts.map +1 -0
  123. package/dist/runtime/query/relation-planner.js +648 -0
  124. package/dist/runtime/query/relation-planner.js.map +1 -0
  125. package/dist/runtime/query/relation-writes.d.ts +2 -1
  126. package/dist/runtime/query/relation-writes.d.ts.map +1 -1
  127. package/dist/runtime/query/relation-writes.js +33 -22
  128. package/dist/runtime/query/relation-writes.js.map +1 -1
  129. package/dist/runtime/query/table-index.d.ts +51 -3
  130. package/dist/runtime/query/table-index.d.ts.map +1 -1
  131. package/dist/runtime/query/table-index.js +119 -4
  132. package/dist/runtime/query/table-index.js.map +1 -1
  133. package/dist/runtime/query/unique.d.ts +3 -2
  134. package/dist/runtime/query/unique.d.ts.map +1 -1
  135. package/dist/runtime/query/unique.js +7 -6
  136. package/dist/runtime/query/unique.js.map +1 -1
  137. package/dist/runtime/query/update.d.ts +2 -0
  138. package/dist/runtime/query/update.d.ts.map +1 -1
  139. package/dist/runtime/query/update.js +98 -30
  140. package/dist/runtime/query/update.js.map +1 -1
  141. package/dist/runtime/query/updated-at.d.ts +6 -4
  142. package/dist/runtime/query/updated-at.d.ts.map +1 -1
  143. package/dist/runtime/query/updated-at.js +23 -9
  144. package/dist/runtime/query/updated-at.js.map +1 -1
  145. package/dist/runtime/query/upsert.d.ts.map +1 -1
  146. package/dist/runtime/query/upsert.js +11 -9
  147. package/dist/runtime/query/upsert.js.map +1 -1
  148. package/dist/runtime/sqlite-open.d.ts +3 -0
  149. package/dist/runtime/sqlite-open.d.ts.map +1 -0
  150. package/dist/runtime/sqlite-open.js +22 -0
  151. package/dist/runtime/sqlite-open.js.map +1 -0
  152. package/dist/runtime/transaction.d.ts +5 -0
  153. package/dist/runtime/transaction.d.ts.map +1 -0
  154. package/dist/runtime/transaction.js +26 -0
  155. package/dist/runtime/transaction.js.map +1 -0
  156. package/dist/runtime/types.d.ts +1 -0
  157. package/dist/runtime/types.d.ts.map +1 -1
  158. package/dist/schema/column.d.ts +1 -1
  159. package/dist/schema/column.d.ts.map +1 -1
  160. package/dist/schema/column.js +1 -1
  161. package/dist/schema/column.js.map +1 -1
  162. package/dist/schema/define-schema.js.map +1 -1
  163. package/dist/schema/index.d.ts +1 -1
  164. package/dist/schema/index.d.ts.map +1 -1
  165. package/dist/schema/index.js +1 -1
  166. package/dist/schema/index.js.map +1 -1
  167. package/dist/schema/types.d.ts +3 -0
  168. package/dist/schema/types.d.ts.map +1 -1
  169. package/dist/sql/builder.d.ts.map +1 -1
  170. package/dist/sql/template.d.ts +7 -0
  171. package/dist/sql/template.d.ts.map +1 -1
  172. package/dist/sql/template.js +89 -3
  173. package/dist/sql/template.js.map +1 -1
  174. package/dist/utils/case.d.ts +4 -0
  175. package/dist/utils/case.d.ts.map +1 -1
  176. package/dist/utils/case.js +11 -0
  177. package/dist/utils/case.js.map +1 -1
  178. package/dist/utils/uuid.js.map +1 -1
  179. package/package.json +21 -4
@@ -1,9 +1,19 @@
1
1
  import { postgresDialect, quoteIdentifier, tableRef, } from "../../dialect/postgres.js";
2
- import { buildFindByIdQuery, buildFindManyQuery, buildSelectColumns, compileOrderBy, compileWhere, isImpossibleWhere, normalizeSelectColumns, rowsToTs, rowsToTsIndexed, rowToTs, rowToTsIndexed, } from "./compile.js";
2
+ import { buildFindByIdQuery, buildFindManyQuery, buildSelectColumns, compileOrderBy, compileWhere, getCachedFindManyQuery, getCachedOrderByClause, getCachedWhereClause, isImpossibleWhere, mapRowToTs, mapRowsToTs, normalizeLimitOffset, normalizeSelectColumns, } from "./compile.js";
3
+ import { rebaseParamRefs } from "../../sql/template.js";
3
4
  import { runQuery, runQueryOne } from "./execute.js";
4
5
  import { findM2M, findRelation, tableOwnsFkColumn, } from "./manifest-lookup.js";
5
6
  import { primaryKeyTsNames, requireScalarPrimaryKey, resolvePkWhere, rowPkKey, targetRelationPkSql, } from "./primary-key.js";
6
- import { getTableIndex } from "./table-index.js";
7
+ import { getTableIndex, columnBySqlName } from "./table-index.js";
8
+ import { buildCountAggregateGroupBy, buildPlanExtraSelectCols, compileCountOrderBy, getCachedFindByIdWithQuery, getCachedRelationPlan, hydrateRowsWithPlan, planRelationLoad, withShapeSignature, } from "./relation-planner.js";
9
+ function isRelationSpec(withSpec) {
10
+ if (typeof withSpec !== "object" || withSpec === null)
11
+ return false;
12
+ return ("select" in withSpec ||
13
+ "orderBy" in withSpec ||
14
+ "limit" in withSpec ||
15
+ "with" in withSpec);
16
+ }
7
17
  function validateDistinctOrderBy(distinct, orderBy) {
8
18
  if (!distinct || distinct.length === 0)
9
19
  return;
@@ -37,13 +47,12 @@ function splitWithSpec(withSpec) {
37
47
  async function loadRelationCounts(executor, runtime, parentTable, parentRows, countSpec) {
38
48
  if (parentRows.length === 0)
39
49
  return;
40
- const { manifest } = runtime;
41
50
  const parentIds = parentRows
42
51
  .map((r) => rowPkKey(r, parentTable))
43
52
  .filter(Boolean);
44
53
  if (parentIds.length === 0)
45
54
  return;
46
- for (const [relationName, spec] of Object.entries(countSpec)) {
55
+ await Promise.all(Object.entries(countSpec).map(async ([relationName, spec]) => {
47
56
  const whereFilter = typeof spec === "object" ? spec.where : undefined;
48
57
  const counts = await countRelationLinks(executor, runtime, parentTable, relationName, parentIds, whereFilter);
49
58
  for (const parent of parentRows) {
@@ -52,9 +61,10 @@ async function loadRelationCounts(executor, runtime, parentTable, parentRows, co
52
61
  bucket[relationName] = counts.get(parentKey) ?? 0;
53
62
  parent["_count"] = bucket;
54
63
  }
55
- }
64
+ }));
56
65
  }
57
66
  async function countRelationLinks(executor, runtime, parentTable, relationName, parentIds, whereFilter) {
67
+ const dialect = runtime.dialect ?? postgresDialect;
58
68
  const { manifest } = runtime;
59
69
  const m2m = findM2M(manifest, parentTable.accessor, relationName);
60
70
  if (m2m) {
@@ -72,18 +82,19 @@ async function countRelationLinks(executor, runtime, parentTable, relationName,
72
82
  let extraWhere = "";
73
83
  let extraParams = [];
74
84
  if (whereFilter) {
75
- const compiled = compileWhere(manifest, targetTable, whereFilter, postgresDialect);
85
+ const compiled = compileWhere(manifest, targetTable, whereFilter, dialect, 1, runtime.tableIndex);
76
86
  if (compiled.sql) {
77
- const adjusted = compiled.sql.replace(/\$(\d+)/g, (_, n) => `$${Number(n) + parentIds.length}`);
87
+ const adjusted = rebaseParamRefs(compiled.sql, parentIds.length);
78
88
  extraWhere = ` AND ${adjusted.replace(/^WHERE\s+/i, "")}`;
79
89
  extraParams = compiled.params;
80
90
  }
81
91
  }
82
- const sql = `SELECT ${fkCol} AS parent_id, COUNT(*)::int AS count FROM ${tableRef(targetTable)} WHERE ${fkCol} IN (${placeholders})${extraWhere} GROUP BY ${fkCol}`;
92
+ const sql = `SELECT ${fkCol} AS parent_id, ${dialect.castToInt("COUNT(*)")} AS count FROM ${tableRef(targetTable)} WHERE ${fkCol} IN (${placeholders})${extraWhere} GROUP BY ${fkCol}`;
83
93
  const rows = await runQuery(executor, runtime, { operation: "select", tableAccessor: targetTable.accessor }, sql, [...parentIds, ...extraParams]);
84
94
  return new Map(rows.map((row) => [String(row.parent_id), row.count]));
85
95
  }
86
96
  async function countM2MLinks(executor, runtime, parentTable, m2m, parentIds, whereFilter) {
97
+ const dialect = runtime.dialect ?? postgresDialect;
87
98
  const { manifest } = runtime;
88
99
  const isLeft = m2m.leftAccessor === parentTable.accessor;
89
100
  const targetAccessor = isLeft ? m2m.rightAccessor : m2m.leftAccessor;
@@ -98,32 +109,27 @@ async function countM2MLinks(executor, runtime, parentTable, m2m, parentIds, whe
98
109
  let extraWhere = "";
99
110
  let extraParams = [];
100
111
  if (whereFilter) {
101
- const compiled = compileWhere(manifest, targetTable, whereFilter, postgresDialect);
112
+ const compiled = compileWhere(manifest, targetTable, whereFilter, dialect, 1, runtime.tableIndex);
102
113
  joinSql = ` JOIN ${tableRef(targetTable)} t ON t.${quoteIdentifier(targetRelationPkSql(targetTable))} = j.${quoteIdentifier(targetFkCol)}`;
103
114
  if (compiled.sql) {
104
- const adjusted = compiled.sql.replace(/\$(\d+)/g, (_, n) => `$${Number(n) + parentIds.length}`);
115
+ const adjusted = rebaseParamRefs(compiled.sql, parentIds.length);
105
116
  extraWhere = ` AND ${adjusted.replace(/^WHERE\s+/i, "")}`;
106
117
  extraParams = compiled.params;
107
118
  }
108
119
  }
109
- const sql = `SELECT j.${quoteIdentifier(parentFkCol)} AS parent_id, COUNT(*)::int AS count FROM ${tableRef(throughTable)} j${joinSql} WHERE j.${quoteIdentifier(parentFkCol)} IN (${placeholders})${extraWhere} GROUP BY j.${quoteIdentifier(parentFkCol)}`;
120
+ const sql = `SELECT j.${quoteIdentifier(parentFkCol)} AS parent_id, ${dialect.castToInt("COUNT(*)")} AS count FROM ${tableRef(throughTable)} j${joinSql} WHERE j.${quoteIdentifier(parentFkCol)} IN (${placeholders})${extraWhere} GROUP BY j.${quoteIdentifier(parentFkCol)}`;
110
121
  const rows = await runQuery(executor, runtime, { operation: "select", tableAccessor: throughTable.accessor }, sql, [...parentIds, ...extraParams]);
111
122
  return new Map(rows.map((row) => [String(row.parent_id), row.count]));
112
123
  }
113
124
  function columnsForSelect(table, withSpec) {
114
- const nestedSpec = typeof withSpec === "object" ? withSpec : undefined;
125
+ const nestedSpec = isRelationSpec(withSpec) ? withSpec : undefined;
115
126
  const selectKeys = normalizeSelectColumns(nestedSpec?.select);
116
127
  return buildSelectColumns(table, selectKeys ? [...selectKeys] : undefined);
117
128
  }
118
- function isM2MRelation(manifest, tableAccessor, relationName) {
119
- return findM2M(manifest, tableAccessor, relationName) !== undefined;
120
- }
121
129
  async function loadNestedRelations(executor, runtime, targetTable, childRows, nestedWith) {
122
130
  if (childRows.length === 0)
123
131
  return;
124
- for (const [nestedName, nestedWithSpec] of Object.entries(nestedWith)) {
125
- await loadOneRelation(executor, runtime, targetTable, childRows, nestedName, nestedWithSpec);
126
- }
132
+ await Promise.all(Object.entries(nestedWith).map(([nestedName, nestedWithSpec]) => loadOneRelation(executor, runtime, targetTable, childRows, nestedName, nestedWithSpec)));
127
133
  }
128
134
  async function loadOneRelation(executor, runtime, parentTable, parentRows, relationName, withSpec) {
129
135
  if (parentRows.length === 0)
@@ -143,7 +149,7 @@ async function loadOneRelation(executor, runtime, parentTable, parentRows, relat
143
149
  const parentIds = parentRows
144
150
  .map((r) => rowPkKey(r, parentTable))
145
151
  .filter(Boolean);
146
- const nestedSpec = typeof withSpec === "object" ? withSpec : undefined;
152
+ const nestedSpec = isRelationSpec(withSpec) ? withSpec : undefined;
147
153
  if (relation.cardinality === "one" &&
148
154
  tableOwnsFkColumn(parentTable, relation)) {
149
155
  const fkValues = parentRows
@@ -159,7 +165,8 @@ async function loadOneRelation(executor, runtime, parentTable, parentRows, relat
159
165
  throw new Error(`No primary key defined for table "${targetTable.accessor}"`);
160
166
  }
161
167
  const rows = await runQuery(executor, runtime, { operation: "select", tableAccessor: targetTable.accessor }, `SELECT ${selectCols} FROM ${tableRef(targetTable)} WHERE ${targetPkCol} IN (${placeholders})`, fkValues);
162
- const mapped = rowsToTs(targetTable, rows);
168
+ const targetTableIndex = getTableIndex(runtime.tableIndex, targetTable.accessor);
169
+ const mapped = mapRowsToTs(targetTableIndex, targetTable, rows);
163
170
  const byId = new Map(mapped.map((r) => [String(r[targetPkTsName]), r]));
164
171
  for (const parent of parentRows) {
165
172
  const fkVal = parent[relation.fkColumn];
@@ -173,14 +180,15 @@ async function loadOneRelation(executor, runtime, parentTable, parentRows, relat
173
180
  const selectCols = columnsForSelect(targetTable, withSpec);
174
181
  let sql = `SELECT ${selectCols} FROM ${tableRef(targetTable)} WHERE ${fkCol} IN (${placeholders})`;
175
182
  if (nestedSpec?.orderBy) {
176
- sql += ` ${compileOrderBy(targetTable, nestedSpec.orderBy)}`;
183
+ sql += ` ${compileOrderBy(targetTable, nestedSpec.orderBy, undefined, runtime.tableIndex)}`;
177
184
  }
178
185
  if (nestedSpec?.limit !== undefined) {
179
- sql += ` LIMIT ${nestedSpec.limit}`;
186
+ sql += ` LIMIT ${normalizeLimitOffset(nestedSpec.limit, "limit")}`;
180
187
  }
181
188
  const rows = await runQuery(executor, runtime, { operation: "select", tableAccessor: targetTable.accessor }, sql, parentIds);
182
- const mapped = rowsToTs(targetTable, rows);
183
- const fkTargetCol = targetTable.columns.find((c) => c.sqlName === relation.fkSqlColumn);
189
+ const targetTableIndex = getTableIndex(runtime.tableIndex, targetTable.accessor);
190
+ const mapped = mapRowsToTs(targetTableIndex, targetTable, rows);
191
+ const fkTargetCol = columnBySqlName(targetTableIndex, targetTable, relation.fkSqlColumn);
184
192
  const fkTsName = fkTargetCol?.tsName ?? relation.fkColumn;
185
193
  const grouped = new Map();
186
194
  for (const row of mapped) {
@@ -240,10 +248,12 @@ async function loadM2MRelation(executor, runtime, parentTable, parentRows, m2m,
240
248
  WHERE j.${quoteIdentifier(parentFkCol)} IN (${placeholders})
241
249
  `.trim();
242
250
  const rows = await runQuery(executor, runtime, { operation: "select", tableAccessor: targetTable.accessor }, sql, parentIds);
251
+ const targetTableIndex = getTableIndex(runtime.tableIndex, targetTable.accessor);
243
252
  const grouped = new Map();
244
253
  for (const row of rows) {
245
254
  const parentId = String(row["_parent_id"]);
246
- const mapped = rowToTs(targetTable, row);
255
+ const { ["_parent_id"]: _parentKey, ...targetRow } = row;
256
+ const mapped = mapRowToTs(targetTableIndex, targetTable, targetRow);
247
257
  let bucket = grouped.get(parentId);
248
258
  if (!bucket) {
249
259
  bucket = [];
@@ -256,76 +266,60 @@ async function loadM2MRelation(executor, runtime, parentTable, parentRows, m2m,
256
266
  parent[relationName] = grouped.get(parentKey) ?? [];
257
267
  }
258
268
  }
259
- function buildJoinClauses(manifest, parentTable, withSpec) {
260
- const joins = [];
261
- const selectCols = [];
262
- const joinedRelations = new Set();
263
- if (!withSpec)
264
- return { joins, selectCols, joinedRelations };
265
- for (const [relationName, spec] of Object.entries(withSpec)) {
266
- if (relationName === "_count")
267
- continue;
268
- const relation = findRelation(parentTable, relationName);
269
- if (!relation)
270
- continue;
271
- if (relation.cardinality !== "one")
272
- continue;
273
- if (!tableOwnsFkColumn(parentTable, relation))
274
- continue;
275
- const m2m = findM2M(manifest, parentTable.accessor, relationName);
276
- if (m2m)
277
- continue;
278
- const nestedSpec = typeof spec === "object" ? spec : undefined;
279
- if (nestedSpec?.with)
280
- continue;
281
- const targetTable = manifest.tables[relation.targetAccessor];
282
- if (!targetTable)
283
- continue;
284
- const alias = `__${relationName}`;
285
- const parentFkCol = quoteIdentifier(relation.fkSqlColumn);
286
- const targetPkCol = quoteIdentifier(targetRelationPkSql(targetTable, relation));
287
- const onClause = `${quoteIdentifier(alias)}.${targetPkCol} = ${tableRef(parentTable)}.${parentFkCol}`;
288
- joins.push(`LEFT JOIN ${tableRef(targetTable)} AS ${quoteIdentifier(alias)} ON ${onClause}`);
289
- const selectKeys = nestedSpec?.select
290
- ? normalizeSelectColumns(nestedSpec.select)
291
- : undefined;
292
- const targetCols = selectKeys && selectKeys.length > 0
293
- ? targetTable.columns.filter((c) => selectKeys.includes(c.tsName))
294
- : targetTable.columns;
295
- for (const col of targetCols) {
296
- const prefixedName = `__${relationName}__${col.sqlName}`;
297
- selectCols.push(`${quoteIdentifier(alias)}.${quoteIdentifier(col.sqlName)} AS ${quoteIdentifier(prefixedName)}`);
298
- }
299
- joinedRelations.add(relationName);
269
+ export async function hydrateAndLoadRelations(executor, runtime, table, rawRows, withSpec, plan) {
270
+ if (rawRows.length === 0)
271
+ return [];
272
+ const resolvedPlan = plan ??
273
+ planRelationLoad(runtime.manifest, table, withSpec, runtime.dialect ?? postgresDialect, runtime.tableIndex);
274
+ let resultRows;
275
+ if (withSpec) {
276
+ resultRows = hydrateRowsWithPlan(runtime, table, rawRows, resolvedPlan);
300
277
  }
301
- return { joins, selectCols, joinedRelations };
278
+ else {
279
+ const tableIndex = getTableIndex(runtime.tableIndex, table.accessor);
280
+ resultRows = mapRowsToTs(tableIndex, table, rawRows);
281
+ }
282
+ const batchWith = resolvedPlan.batchWith;
283
+ if (Object.keys(batchWith).length > 0) {
284
+ await loadRelations(executor, runtime, table, resultRows, batchWith);
285
+ }
286
+ return resultRows;
302
287
  }
303
- function extractJoinedRelations(row, manifest, parentTable, joinedRelations) {
304
- const relations = {};
305
- for (const relationName of joinedRelations) {
306
- const relation = findRelation(parentTable, relationName);
307
- if (!relation)
308
- continue;
309
- const targetTable = manifest.tables[relation.targetAccessor];
310
- if (!targetTable)
311
- continue;
312
- const prefix = `__${relationName}__`;
313
- const prefixedKeys = Object.keys(row).filter((k) => k.startsWith(prefix));
314
- if (prefixedKeys.length === 0)
315
- continue;
316
- const allNull = prefixedKeys.every((k) => row[k] == null);
317
- if (allNull) {
318
- relations[relationName] = null;
319
- }
320
- else {
321
- const raw = {};
322
- for (const key of prefixedKeys) {
323
- raw[key.slice(prefix.length)] = row[key];
324
- }
325
- relations[relationName] = rowToTs(targetTable, raw);
326
- }
288
+ async function executeFindManyWithRelations(executor, runtime, tableAccessor, table, tableIndex, args, whereSql, params, planOptions) {
289
+ const dialect = runtime.dialect ?? postgresDialect;
290
+ const { manifest } = runtime;
291
+ const plan = getCachedRelationPlan(manifest, table, args.with, dialect, runtime.tableIndex, planOptions);
292
+ const needsQualifiedRefs = plan.joins.length > 0 ||
293
+ plan.countAggregate !== undefined ||
294
+ plan.hasManyAggregate !== undefined ||
295
+ plan.joinedRelations.size > 0;
296
+ if (needsQualifiedRefs && whereSql) {
297
+ const qualifiedWhere = getCachedWhereClause(manifest, table, args.where, dialect, 1, runtime.tableIndex, true);
298
+ whereSql = qualifiedWhere.sql;
299
+ params = qualifiedWhere.params;
300
+ }
301
+ const extraSelectCols = buildPlanExtraSelectCols(manifest, table, plan, dialect, runtime.tableIndex);
302
+ const countOrderSql = compileCountOrderBy(manifest, table, args.orderBy, plan, runtime.tableIndex);
303
+ const orderSqlForWith = countOrderSql ||
304
+ getCachedOrderByClause(table, args.orderBy, needsQualifiedRefs ? table.sqlName : undefined, runtime.tableIndex);
305
+ const joinClauses = plan.countAggregate && plan.countAggregate.joins.length > 0
306
+ ? plan.countAggregate.joins
307
+ : plan.hasManyAggregate && plan.hasManyAggregate.joins.length > 0
308
+ ? plan.hasManyAggregate.joins
309
+ : plan.joins.length > 0
310
+ ? plan.joins
311
+ : undefined;
312
+ const groupBySql = buildCountAggregateGroupBy(plan);
313
+ const distinctOn = normalizeSelectColumns(args.distinct);
314
+ if (distinctOn && distinctOn.length > 0 && dialect.name === "sqlite") {
315
+ throw new Error("distinct is not supported on SQLite (DISTINCT ON is PostgreSQL-only). Use groupBy or orderBy + a manual query instead.");
327
316
  }
328
- return relations;
317
+ const withSignature = withShapeSignature(args.with);
318
+ const planMode = planOptions?.useHasManyAggregate === false ? "corr" : "agg";
319
+ const signature = `${whereSql}|${orderSqlForWith}|${args.limit ?? ""}|${args.offset ?? ""}|${distinctOn?.join(",") ?? ""}|${withSignature}|${planMode}|${groupBySql}`;
320
+ const query = getCachedFindManyQuery(tableIndex, signature, () => buildFindManyQuery(table, whereSql, orderSqlForWith, args.limit, args.offset, distinctOn, extraSelectCols.length > 0 ? extraSelectCols : undefined, joinClauses, runtime.tableIndex, groupBySql || undefined));
321
+ const rows = await runQuery(executor, runtime, { operation: "select", tableAccessor }, query, params);
322
+ return await hydrateAndLoadRelations(executor, runtime, table, rows, args.with, plan);
329
323
  }
330
324
  export async function loadRelations(executor, runtime, table, rows, withSpec) {
331
325
  if (!withSpec || rows.length === 0)
@@ -338,6 +332,7 @@ export async function loadRelations(executor, runtime, table, rows, withSpec) {
338
332
  return rows;
339
333
  }
340
334
  export async function findMany(executor, runtime, tableAccessor, args) {
335
+ const dialect = runtime.dialect ?? postgresDialect;
341
336
  const { manifest } = runtime;
342
337
  const table = manifest.tables[tableAccessor];
343
338
  if (!table)
@@ -352,51 +347,64 @@ export async function findMany(executor, runtime, tableAccessor, args) {
352
347
  args?.offset === undefined;
353
348
  if (isSimpleFind && tableIndex) {
354
349
  const rows = await runQuery(executor, runtime, queryCtx, tableIndex.findAllSql, []);
355
- return rowsToTsIndexed(tableIndex, table, rows);
350
+ return mapRowsToTs(tableIndex, table, rows);
356
351
  }
357
352
  const distinctOn = normalizeSelectColumns(args?.distinct);
358
353
  validateDistinctOrderBy(distinctOn, args?.orderBy);
359
- const compiledWhere = compileWhere(manifest, table, args?.where, postgresDialect);
354
+ if (distinctOn && distinctOn.length > 0 && dialect.name === "sqlite") {
355
+ throw new Error("distinct is not supported on SQLite (DISTINCT ON is PostgreSQL-only). Use groupBy or orderBy + a manual query instead.");
356
+ }
357
+ const compiledWhere = getCachedWhereClause(manifest, table, args?.where, dialect, 1, runtime.tableIndex);
360
358
  if (compiledWhere.impossible || isImpossibleWhere(compiledWhere.sql)) {
361
359
  return [];
362
360
  }
363
- const { sql: whereSql, params } = compiledWhere;
364
- const orderSql = compileOrderBy(table, args?.orderBy);
365
- const { relationWith, countSpec } = args?.with
366
- ? splitWithSpec(args.with)
367
- : { relationWith: {} };
368
- const { joins, selectCols, joinedRelations } = buildJoinClauses(manifest, table, relationWith);
369
- const query = buildFindManyQuery(table, whereSql, orderSql, args?.limit, args?.offset, distinctOn, selectCols.length > 0 ? selectCols : undefined, joins.length > 0 ? joins : undefined);
370
- const rows = await runQuery(executor, runtime, { operation: "select", tableAccessor }, query, params);
371
- let resultRows;
372
- if (joinedRelations.size > 0) {
373
- resultRows = rows.map((rawRow) => {
374
- const relations = extractJoinedRelations(rawRow, manifest, table, joinedRelations);
375
- return { ...rowToTs(table, rawRow), ...relations };
376
- });
377
- }
378
- else {
379
- resultRows = tableIndex
380
- ? rowsToTsIndexed(tableIndex, table, rows)
381
- : rowsToTs(table, rows);
361
+ let whereSql = compiledWhere.sql;
362
+ let params = compiledWhere.params;
363
+ const hasWith = Boolean(args?.with && Object.keys(args.with).length > 0);
364
+ if (!hasWith) {
365
+ const orderSql = getCachedOrderByClause(table, args?.orderBy, undefined, runtime.tableIndex);
366
+ const signature = `${whereSql}|${orderSql}|${args?.limit ?? ""}|${args?.offset ?? ""}|${distinctOn?.join(",") ?? ""}`;
367
+ const query = getCachedFindManyQuery(tableIndex, signature, () => buildFindManyQuery(table, whereSql, orderSql, args?.limit, args?.offset, distinctOn, undefined, undefined, runtime.tableIndex));
368
+ const rows = await runQuery(executor, runtime, { operation: "select", tableAccessor }, query, params);
369
+ return mapRowsToTs(tableIndex, table, rows);
382
370
  }
383
- const remainingWith = {};
384
- for (const [key, spec] of Object.entries(relationWith)) {
385
- if (!joinedRelations.has(key)) {
386
- remainingWith[key] = spec;
371
+ return executeFindManyWithRelations(executor, runtime, tableAccessor, table, tableIndex, { ...(args ?? {}), with: args.with }, whereSql, params, { useHasManyAggregate: true });
372
+ }
373
+ export async function findFirst(executor, runtime, tableAccessor, args) {
374
+ const dialect = runtime.dialect ?? postgresDialect;
375
+ const hasWith = Boolean(args?.with && Object.keys(args.with).length > 0);
376
+ const canFastPath = !hasWith && !args?.distinct && args?.offset === undefined;
377
+ if (canFastPath) {
378
+ const { manifest } = runtime;
379
+ const table = manifest.tables[tableAccessor];
380
+ if (!table)
381
+ throw new Error(`Unknown table: ${tableAccessor}`);
382
+ const tableIndex = getTableIndex(runtime.tableIndex, tableAccessor);
383
+ const compiledWhere = getCachedWhereClause(manifest, table, args?.where, dialect, 1, runtime.tableIndex);
384
+ if (compiledWhere.impossible || isImpossibleWhere(compiledWhere.sql)) {
385
+ return null;
387
386
  }
387
+ const { sql: whereSql, params } = compiledWhere;
388
+ const orderSql = getCachedOrderByClause(table, args?.orderBy, undefined, runtime.tableIndex);
389
+ const signature = `${whereSql}|${orderSql}|1||`;
390
+ const query = getCachedFindManyQuery(tableIndex, signature, () => buildFindManyQuery(table, whereSql, orderSql, 1, undefined, undefined, undefined, undefined, runtime.tableIndex));
391
+ const rows = await runQuery(executor, runtime, { operation: "select", tableAccessor }, query, params);
392
+ if (rows.length === 0)
393
+ return null;
394
+ return mapRowToTs(tableIndex, table, rows[0]);
388
395
  }
389
- if (countSpec) {
390
- remainingWith._count = countSpec;
396
+ const { manifest } = runtime;
397
+ const table = manifest.tables[tableAccessor];
398
+ if (!table)
399
+ throw new Error(`Unknown table: ${tableAccessor}`);
400
+ const tableIndex = getTableIndex(runtime.tableIndex, tableAccessor);
401
+ const compiledWhere = getCachedWhereClause(manifest, table, args?.where, dialect, 1, runtime.tableIndex);
402
+ if (compiledWhere.impossible || isImpossibleWhere(compiledWhere.sql)) {
403
+ return null;
391
404
  }
392
- await loadRelations(executor, runtime, table, resultRows, Object.keys(remainingWith).length > 0 ? remainingWith : undefined);
393
- return resultRows;
394
- }
395
- export async function findFirst(executor, runtime, tableAccessor, args) {
396
- const rows = await findMany(executor, runtime, tableAccessor, {
397
- ...args,
398
- limit: 1,
399
- });
405
+ const distinctOn = normalizeSelectColumns(args?.distinct);
406
+ validateDistinctOrderBy(distinctOn, args?.orderBy);
407
+ const rows = await executeFindManyWithRelations(executor, runtime, tableAccessor, table, tableIndex, { ...args, with: args.with, limit: 1 }, compiledWhere.sql, compiledWhere.params, { useHasManyAggregate: false });
400
408
  return rows[0] ?? null;
401
409
  }
402
410
  function extractScalarPkValue(table, id) {
@@ -404,131 +412,50 @@ function extractScalarPkValue(table, id) {
404
412
  const { tsName } = requireScalarPrimaryKey(table);
405
413
  return where[tsName];
406
414
  }
407
- function inlineRelationColumnAlias(relationName) {
408
- return `__neoorm_${relationName}`;
409
- }
410
- function tryGetInlineManyRelationLoad(manifest, table, withSpec) {
411
- if (!withSpec)
412
- return null;
413
- const { relationWith, countSpec } = splitWithSpec(withSpec);
414
- if (countSpec)
415
- return null;
416
- const entries = Object.entries(relationWith);
417
- if (entries.length !== 1)
418
- return null;
419
- const [relationName, withInput] = entries[0];
420
- if (isM2MRelation(manifest, table.accessor, relationName))
421
- return null;
422
- const relation = findRelation(table, relationName);
423
- if (!relation || relation.cardinality !== "many")
424
- return null;
425
- if (tableOwnsFkColumn(table, relation))
426
- return null;
427
- const nestedSpec = typeof withInput === "object" ? withInput : undefined;
428
- if (nestedSpec?.with)
429
- return null;
430
- const targetTable = manifest.tables[relation.targetAccessor];
431
- if (!targetTable)
432
- return null;
433
- return { relationName, relation, targetTable, nestedSpec };
434
- }
435
- const INLINE_RELATION_PARENT_ALIAS = "u";
436
- const INLINE_RELATION_CHILD_ALIAS = "r";
437
- function buildInlineManyRelationSubquery(parentAlias, parentTable, relation, targetTable, nestedSpec) {
438
- const { sqlName: parentPkSql } = requireScalarPrimaryKey(parentTable);
439
- const parentPkCol = quoteIdentifier(parentPkSql);
440
- const fkCol = quoteIdentifier(relation.fkSqlColumn);
441
- const childAlias = INLINE_RELATION_CHILD_ALIAS;
442
- const childRef = tableRef(targetTable);
443
- const parentRef = quoteIdentifier(parentAlias);
444
- let sql = `(SELECT json_agg(${quoteIdentifier(childAlias)}.*) FROM ${childRef} ${quoteIdentifier(childAlias)} WHERE ${quoteIdentifier(childAlias)}.${fkCol} = ${parentRef}.${parentPkCol}`;
445
- if (nestedSpec?.orderBy) {
446
- sql += ` ${compileOrderBy(targetTable, nestedSpec.orderBy, childAlias)}`;
447
- }
448
- if (nestedSpec?.limit !== undefined) {
449
- sql += ` LIMIT ${nestedSpec.limit}`;
450
- }
451
- const alias = quoteIdentifier(inlineRelationColumnAlias(relation.name));
452
- return `${sql}) AS ${alias}`;
453
- }
454
- function buildFindByIdWithInlineRelationQuery(table, inlineSubquery) {
455
- const parentAlias = INLINE_RELATION_PARENT_ALIAS;
456
- const { sqlName } = requireScalarPrimaryKey(table);
457
- const pkCol = quoteIdentifier(sqlName);
458
- const ref = quoteIdentifier(parentAlias);
459
- return `SELECT ${ref}.*, ${inlineSubquery} FROM ${tableRef(table)} ${ref} WHERE ${ref}.${pkCol} = $1 LIMIT 1`;
460
- }
461
- function hydrateInlineManyRelationRow(parentIndex, targetIndex, parentTable, targetTable, relationName, rawRow) {
462
- const alias = inlineRelationColumnAlias(relationName);
463
- const jsonVal = rawRow[alias];
464
- const parentRaw = {};
465
- for (const col of parentTable.columns) {
466
- if (col.sqlName in rawRow) {
467
- parentRaw[col.sqlName] = rawRow[col.sqlName];
468
- }
469
- }
470
- const parent = parentIndex
471
- ? rowToTsIndexed(parentIndex, parentTable, parentRaw)
472
- : rowToTs(parentTable, parentRaw);
473
- let children = [];
474
- if (jsonVal != null) {
475
- const parsed = typeof jsonVal === "string"
476
- ? JSON.parse(jsonVal)
477
- : jsonVal;
478
- if (Array.isArray(parsed)) {
479
- children = parsed.map((row) => {
480
- const childRow = row;
481
- return targetIndex
482
- ? rowToTsIndexed(targetIndex, targetTable, childRow)
483
- : rowToTs(targetTable, childRow);
484
- });
485
- }
486
- }
487
- parent[relationName] = children;
488
- return parent;
489
- }
490
415
  export async function findById(executor, runtime, tableAccessor, id, args) {
416
+ const dialect = runtime.dialect ?? postgresDialect;
491
417
  const { manifest } = runtime;
492
418
  const table = manifest.tables[tableAccessor];
493
419
  if (!table)
494
420
  throw new Error(`Unknown table: ${tableAccessor}`);
421
+ const tableIndex = getTableIndex(runtime.tableIndex, tableAccessor);
495
422
  if (table.primaryKey.length !== 1) {
496
423
  const where = resolvePkWhere(table, id);
497
- const findArgs = {
424
+ if (args?.with) {
425
+ const compiledWhere = getCachedWhereClause(manifest, table, where, dialect, 1, runtime.tableIndex);
426
+ if (compiledWhere.impossible || isImpossibleWhere(compiledWhere.sql)) {
427
+ return null;
428
+ }
429
+ const rows = await executeFindManyWithRelations(executor, runtime, tableAccessor, table, tableIndex, { where, limit: 1, with: args.with }, compiledWhere.sql, compiledWhere.params, { useHasManyAggregate: false });
430
+ return rows[0] ?? null;
431
+ }
432
+ const rows = await findMany(executor, runtime, tableAccessor, {
498
433
  where,
499
434
  limit: 1,
500
- };
501
- if (args?.with !== undefined) {
502
- findArgs.with = args.with;
503
- }
504
- const rows = await findMany(executor, runtime, tableAccessor, findArgs);
435
+ });
505
436
  return rows[0] ?? null;
506
437
  }
507
438
  const pkValue = extractScalarPkValue(table, id);
508
- const tableIndex = getTableIndex(runtime.tableIndex, tableAccessor);
509
439
  const ctx = { operation: "select", tableAccessor };
510
440
  if (!args?.with) {
511
441
  const query = tableIndex?.findByIdSql || buildFindByIdQuery(table);
512
442
  const row = await runQueryOne(executor, runtime, ctx, query, [pkValue]);
513
- return row ? (tableIndex ? rowToTsIndexed(tableIndex, table, row) : rowToTs(table, row)) : null;
443
+ return row ? mapRowToTs(tableIndex, table, row) : null;
514
444
  }
515
- const inlineLoad = tryGetInlineManyRelationLoad(manifest, table, args.with);
516
- if (inlineLoad) {
517
- const subquery = buildInlineManyRelationSubquery(INLINE_RELATION_PARENT_ALIAS, table, inlineLoad.relation, inlineLoad.targetTable, inlineLoad.nestedSpec);
518
- const query = buildFindByIdWithInlineRelationQuery(table, subquery);
519
- const row = await runQueryOne(executor, runtime, ctx, query, [pkValue]);
520
- if (!row)
445
+ const cached = getCachedFindByIdWithQuery(manifest, table, args.with, dialect, runtime.tableIndex);
446
+ if (cached) {
447
+ const rows = await runQuery(executor, runtime, ctx, cached.sql, [pkValue]);
448
+ if (rows.length === 0)
521
449
  return null;
522
- const targetIndex = getTableIndex(runtime.tableIndex, inlineLoad.targetTable.accessor);
523
- return hydrateInlineManyRelationRow(tableIndex, targetIndex, table, inlineLoad.targetTable, inlineLoad.relationName, row);
450
+ const hydrated = hydrateRowsWithPlan(runtime, table, rows, cached.plan);
451
+ return hydrated[0] ?? null;
524
452
  }
525
453
  const where = resolvePkWhere(table, id);
526
- const findArgs = {
527
- where,
528
- limit: 1,
529
- with: args.with,
530
- };
531
- const rows = await findMany(executor, runtime, tableAccessor, findArgs);
454
+ const compiledWhere = getCachedWhereClause(manifest, table, where, dialect, 1, runtime.tableIndex);
455
+ if (compiledWhere.impossible || isImpossibleWhere(compiledWhere.sql)) {
456
+ return null;
457
+ }
458
+ const rows = await executeFindManyWithRelations(executor, runtime, tableAccessor, table, tableIndex, { where, limit: 1, with: args.with }, compiledWhere.sql, compiledWhere.params, { useHasManyAggregate: false });
532
459
  return rows[0] ?? null;
533
460
  }
534
461
  //# sourceMappingURL=find.js.map