@vibeorm/runtime 1.1.5 → 1.1.7

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/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@vibeorm/runtime",
3
- "version": "1.1.5",
3
+ "version": "1.1.7",
4
4
  "description": "Driver-agnostic query engine and client runtime for VibeORM",
5
5
  "license": "MIT",
6
6
  "keywords": [
@@ -12,20 +12,29 @@
12
12
  * __lat_1."__vibeorm_rel_profile"
13
13
  * FROM "User"
14
14
  * LEFT JOIN LATERAL (
15
- * SELECT COALESCE(json_agg(json_build_object(
16
- * 'id', __rel."id", 'title', __rel."title", ...
17
- * )), '[]'::json) AS "__vibeorm_rel_posts"
18
- * FROM "Post" __rel WHERE __rel."authorId" = "User"."id"
15
+ * SELECT COALESCE(jsonb_agg(to_jsonb("__sub".*)), '[]'::jsonb)
16
+ * AS "__vibeorm_rel_posts"
17
+ * FROM (
18
+ * SELECT __rel."id" AS "id", __rel."title" AS "title", ...
19
+ * FROM "Post" __rel WHERE __rel."authorId" = "User"."id"
20
+ * ) "__sub"
19
21
  * ) __lat_0 ON true
20
22
  * LEFT JOIN LATERAL (
21
- * SELECT json_build_object(
22
- * 'id', __rel."id", 'bio', __rel."bio", ...
23
- * ) AS "__vibeorm_rel_profile"
24
- * FROM "Profile" __rel WHERE __rel."userId" = "User"."id" LIMIT 1
23
+ * SELECT to_jsonb("__sub".*) AS "__vibeorm_rel_profile"
24
+ * FROM (
25
+ * SELECT __rel."id" AS "id", __rel."bio" AS "bio", ...
26
+ * FROM "Profile" __rel WHERE __rel."userId" = "User"."id"
27
+ * LIMIT 1
28
+ * ) "__sub"
25
29
  * ) __lat_1 ON true
26
30
  * WHERE "User"."role" = $1
27
31
  * ORDER BY "User"."id" ASC
28
32
  * LIMIT 20
33
+ *
34
+ * Uses to_jsonb() on a subselect instead of json_build_object() to avoid
35
+ * PostgreSQL's 100-argument function limit (models with >50 scalar fields
36
+ * would exceed this). Column aliases in the inner SELECT handle the
37
+ * dbName→tsName remapping at zero cost.
29
38
  */
30
39
 
31
40
  import type {
@@ -183,14 +192,16 @@ export function buildLateralJoinQuery(params: {
183
192
  const relatedModelMeta = modelMap.get(relationMeta.relatedModel);
184
193
  if (!relatedModelMeta) continue;
185
194
 
186
- // Build json_build_object fields for the related model.
195
+ // Build aliased column list for the inner subquery.
196
+ // Uses to_jsonb("__sub".*) instead of json_build_object() to avoid
197
+ // PostgreSQL's 100-argument function limit on wide models.
187
198
  // Respect nested select/omit to avoid serialising columns the caller doesn't need.
188
199
  const relatedScalars = resolveRelatedScalars({
189
200
  relatedModelMeta,
190
201
  nestedArgs,
191
202
  });
192
- const jsonFields = relatedScalars
193
- .map((f) => `'${f.name}', ${relatedAlias}."${f.dbName}"`)
203
+ const columnAliases = relatedScalars
204
+ .map((f) => `${relatedAlias}."${f.dbName}" AS "${f.name}"`)
194
205
  .join(", ");
195
206
 
196
207
  let lateralSubquery: string;
@@ -288,22 +299,27 @@ export function buildLateralJoinQuery(params: {
288
299
  allValues.push(nestedArgs.skip);
289
300
  }
290
301
 
291
- // When take or skip is specified, use a subquery to apply per-parent LIMIT/OFFSET properly
302
+ // Wrap in inner subquery with column aliases + to_jsonb to avoid
303
+ // PostgreSQL's 100-argument limit on json_build_object.
292
304
  if (nestedArgs.take !== undefined || nestedArgs.skip !== undefined) {
293
305
  lateralSubquery = `LEFT JOIN LATERAL (
294
- SELECT COALESCE(jsonb_agg(json_build_object(${jsonFields})${orderBySql}), '[]'::jsonb) AS "${colAlias}"
306
+ SELECT COALESCE(jsonb_agg(to_jsonb("__sub".*)), '[]'::jsonb) AS "${colAlias}"
295
307
  FROM (
296
- SELECT ${relatedAlias}.* FROM "${relatedModelMeta.dbName}" ${relatedAlias}
308
+ SELECT ${columnAliases}
309
+ FROM "${relatedModelMeta.dbName}" ${relatedAlias}
297
310
  ${junctionJoin}
298
311
  WHERE ${subWhere}${nestedWhereSql}${orderBySql}${limitSql}${offsetSql}
299
- ) ${relatedAlias}
312
+ ) "__sub"
300
313
  ) ${alias} ON true`;
301
314
  } else {
302
315
  lateralSubquery = `LEFT JOIN LATERAL (
303
- SELECT COALESCE(jsonb_agg(json_build_object(${jsonFields})${orderBySql}), '[]'::jsonb) AS "${colAlias}"
304
- FROM "${relatedModelMeta.dbName}" ${relatedAlias}
305
- ${junctionJoin}
306
- WHERE ${subWhere}${nestedWhereSql}
316
+ SELECT COALESCE(jsonb_agg(to_jsonb("__sub".*)), '[]'::jsonb) AS "${colAlias}"
317
+ FROM (
318
+ SELECT ${columnAliases}
319
+ FROM "${relatedModelMeta.dbName}" ${relatedAlias}
320
+ ${junctionJoin}
321
+ WHERE ${subWhere}${nestedWhereSql}${orderBySql}
322
+ ) "__sub"
307
323
  ) ${alias} ON true`;
308
324
  }
309
325
 
@@ -396,24 +412,29 @@ export function buildLateralJoinQuery(params: {
396
412
  allValues.push(nestedArgs.skip);
397
413
  }
398
414
 
399
- // When take or skip is specified, use a subquery to apply per-parent LIMIT/OFFSET properly
415
+ // Wrap in inner subquery with column aliases + to_jsonb to avoid
416
+ // PostgreSQL's 100-argument limit on json_build_object.
400
417
  if (nestedArgs.take !== undefined || nestedArgs.skip !== undefined) {
401
418
  lateralSubquery = `LEFT JOIN LATERAL (
402
- SELECT COALESCE(jsonb_agg(json_build_object(${jsonFields})${orderBySql}), '[]'::jsonb) AS "${colAlias}"
419
+ SELECT COALESCE(jsonb_agg(to_jsonb("__sub".*)), '[]'::jsonb) AS "${colAlias}"
403
420
  FROM (
404
- SELECT * FROM "${relatedModelMeta.dbName}" ${relatedAlias}
421
+ SELECT ${columnAliases}
422
+ FROM "${relatedModelMeta.dbName}" ${relatedAlias}
405
423
  WHERE ${subWhere}${nestedWhereSql}${orderBySql}${limitSql}${offsetSql}
406
- ) ${relatedAlias}
424
+ ) "__sub"
407
425
  ) ${alias} ON true`;
408
426
  } else {
409
427
  lateralSubquery = `LEFT JOIN LATERAL (
410
- SELECT COALESCE(jsonb_agg(json_build_object(${jsonFields})${orderBySql}), '[]'::jsonb) AS "${colAlias}"
411
- FROM "${relatedModelMeta.dbName}" ${relatedAlias}
412
- WHERE ${subWhere}${nestedWhereSql}
428
+ SELECT COALESCE(jsonb_agg(to_jsonb("__sub".*)), '[]'::jsonb) AS "${colAlias}"
429
+ FROM (
430
+ SELECT ${columnAliases}
431
+ FROM "${relatedModelMeta.dbName}" ${relatedAlias}
432
+ WHERE ${subWhere}${nestedWhereSql}${orderBySql}
433
+ ) "__sub"
413
434
  ) ${alias} ON true`;
414
435
  }
415
436
  } else {
416
- // To-one: use json_build_object with LIMIT 1
437
+ // To-one: use to_jsonb on subselect with LIMIT 1
417
438
  let joinCondition: string;
418
439
 
419
440
  if (fkTable === "parent") {
@@ -432,10 +453,13 @@ export function buildLateralJoinQuery(params: {
432
453
  }
433
454
 
434
455
  lateralSubquery = `LEFT JOIN LATERAL (
435
- SELECT json_build_object(${jsonFields}) AS "${colAlias}"
436
- FROM "${relatedModelMeta.dbName}" ${relatedAlias}
437
- WHERE ${joinCondition}
438
- LIMIT 1
456
+ SELECT to_jsonb("__sub".*) AS "${colAlias}"
457
+ FROM (
458
+ SELECT ${columnAliases}
459
+ FROM "${relatedModelMeta.dbName}" ${relatedAlias}
460
+ WHERE ${joinCondition}
461
+ LIMIT 1
462
+ ) "__sub"
439
463
  ) ${alias} ON true`;
440
464
  }
441
465
  }
@@ -886,7 +910,7 @@ export function whereHasRelationFilter(params: {
886
910
  }
887
911
 
888
912
  /**
889
- * Resolve which scalar columns to include in a lateral join's json_build_object.
913
+ * Resolve which scalar columns to include in a lateral join's inner subquery.
890
914
  * Respects nested select/omit so we don't serialise unnecessary columns.
891
915
  * Always includes the PK so nested relation loading can stitch correctly.
892
916
  */