orchid-orm 1.40.1 → 1.41.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.
package/dist/index.mjs CHANGED
@@ -1,6 +1,6 @@
1
- import { makeColumnTypes, QueryHooks, defaultSchemaConfig, raw, getColumnTypes, parseTableData, _queryHookAfterCreate, _queryHookAfterUpdate, getQueryAs, setQueryObjectValue, pushQueryOnForOuter, VirtualColumn, pushQueryValue, _queryCreateMany, isQueryReturnsAll, _queryHookBeforeUpdate, _queryFindBy, _queryCreate, _queryRows, _queryUpdate, _queryDelete, _queryDefaults, _queryUpdateOrThrow, _queryWhere, _queryJoinOn, OrchidOrmInternalError, _queryCreateFrom, NotFoundError, _queryFindByOptional, _querySelect, _queryWhereExists, _queryTake, _queryTakeOptional, Adapter, _initQueryBuilder, Db, getClonedQueryData } from 'pqb';
1
+ import { makeColumnTypes, QueryHooks, defaultSchemaConfig, raw, getColumnTypes, parseTableData, _queryHookAfterCreate, _queryHookAfterUpdate, getQueryAs, setQueryObjectValueImmutable, pushQueryOnForOuter, _queryWhere, _queryDefaults, VirtualColumn, pushQueryValueImmutable, _queryCreateMany, isQueryReturnsAll, _queryHookBeforeUpdate, _queryFindBy, _queryCreate, _queryRows, _queryUpdate, _queryDelete, _queryUpdateOrThrow, OrchidOrmInternalError, _queryJoinOn, _queryCreateFrom, NotFoundError, _queryFindByOptional, _querySelect, _queryWhereExists, _queryTake, _queryTakeOptional, Adapter, _initQueryBuilder, Db, getClonedQueryData } from 'pqb';
2
2
  export * from 'pqb';
3
- import { getStackTrace, applyMixins, getCallerFilePath, snakeCaseKey, toSnakeCase, emptyObject, emptyArray, objectHasValues, toArray } from 'orchid-core';
3
+ import { getStackTrace, applyMixins, getCallerFilePath, snakeCaseKey, toSnakeCase, emptyObject, emptyArray, toArray, objectHasValues, pick } from 'orchid-core';
4
4
  export * from 'orchid-core';
5
5
  import { AsyncLocalStorage } from 'node:async_hooks';
6
6
 
@@ -181,7 +181,7 @@ const hasRelationHandleCreate = (q, ctx, item, rowIndex, key, primaryKeys, neste
181
181
  };
182
182
  const hasRelationHandleUpdate = (q, set, key, primaryKeys, nestedUpdate) => {
183
183
  const value = set[key];
184
- if (!value.set && !("upsert" in value) && (!value.disconnect || Array.isArray(value.disconnect) && value.disconnect.length === 0) && (!value.delete || Array.isArray(value.delete) && value.delete.length === 0) && (!value.update || Array.isArray(value.update.where) && value.update.where.length === 0) && (!value.create || Array.isArray(value.create) && value.create.length === 0))
184
+ if (!value.set && !("upsert" in value) && (!value.add || Array.isArray(value.add) && value.add.length === 0) && (!value.disconnect || Array.isArray(value.disconnect) && value.disconnect.length === 0) && (!value.delete || Array.isArray(value.delete) && value.delete.length === 0) && (!value.update || Array.isArray(value.update.where) && value.update.where.length === 0) && (!value.create || Array.isArray(value.create) && value.create.length === 0))
185
185
  return;
186
186
  selectIfNotSelected(q, primaryKeys);
187
187
  q.q.wrapInTransaction = true;
@@ -204,13 +204,6 @@ const selectIfNotSelected = (q, columns) => {
204
204
  q.q.select = select;
205
205
  }
206
206
  };
207
- const relationWhere = (len, keys, valueKeys) => (params) => {
208
- const obj = {};
209
- for (let i = 0; i < len; i++) {
210
- obj[keys[i]] = params[valueKeys[i]];
211
- }
212
- return obj;
213
- };
214
207
  function joinHasThrough(q, baseQuery, joiningQuery, throughRelation, sourceRelation) {
215
208
  return q.whereExists(
216
209
  throughRelation.joinQuery(throughRelation.query, baseQuery),
@@ -224,14 +217,9 @@ function joinHasThrough(q, baseQuery, joiningQuery, throughRelation, sourceRelat
224
217
  );
225
218
  }
226
219
  function joinHasRelation(baseQuery, joiningQuery, primaryKeys, foreignKeys, len) {
227
- const q = joiningQuery.clone();
228
- setQueryObjectValue(
229
- q,
230
- "joinedShapes",
231
- baseQuery.q.as || baseQuery.table,
232
- baseQuery.q.shape
233
- );
234
220
  const baseAs = getQueryAs(baseQuery);
221
+ const q = joiningQuery.clone();
222
+ setQueryObjectValueImmutable(q, "joinedShapes", baseAs, baseQuery.q.shape);
235
223
  for (let i = 0; i < len; i++) {
236
224
  pushQueryOnForOuter(
237
225
  q,
@@ -340,7 +328,7 @@ class BelongsToVirtualColumn extends VirtualColumn {
340
328
  const relationData = [values];
341
329
  store.belongsTo[key] = relationData;
342
330
  q.q.wrapInTransaction = true;
343
- pushQueryValue(q, "beforeCreate", async (q2) => {
331
+ pushQueryValueImmutable(q, "beforeCreate", async (q2) => {
344
332
  const inserted = await this.nestedInsert(
345
333
  q2,
346
334
  relationData.map(([, , data]) => data)
@@ -364,9 +352,13 @@ class BelongsToVirtualColumn extends VirtualColumn {
364
352
  const makeBelongsToMethod = (tableConfig, table, relation, relationName, query) => {
365
353
  const primaryKeys = relation.options.references;
366
354
  const foreignKeys = relation.options.columns;
355
+ const { on } = relation.options;
356
+ if (on) {
357
+ _queryWhere(query, [on]);
358
+ _queryDefaults(query, on);
359
+ }
367
360
  const len = primaryKeys.length;
368
- const state = { query, primaryKeys, foreignKeys, len };
369
- const makeWhere = relationWhere(len, primaryKeys, foreignKeys);
361
+ const state = { query, primaryKeys, foreignKeys, len, on };
370
362
  addAutoForeignKey(
371
363
  tableConfig,
372
364
  table,
@@ -376,14 +368,9 @@ const makeBelongsToMethod = (tableConfig, table, relation, relationName, query)
376
368
  relation.options
377
369
  );
378
370
  const join = (baseQuery, joiningQuery, primaryKeys2, foreignKeys2) => {
379
- const q = joiningQuery.clone();
380
- setQueryObjectValue(
381
- q,
382
- "joinedShapes",
383
- baseQuery.q.as || baseQuery.table,
384
- baseQuery.q.shape
385
- );
386
371
  const baseAs = getQueryAs(baseQuery);
372
+ const q = joiningQuery.clone();
373
+ setQueryObjectValueImmutable(q, "joinedShapes", baseAs, baseQuery.q.shape);
387
374
  for (let i = 0; i < len; i++) {
388
375
  pushQueryOnForOuter(
389
376
  q,
@@ -406,7 +393,11 @@ const makeBelongsToMethod = (tableConfig, table, relation, relationName, query)
406
393
  return {
407
394
  returns: "one",
408
395
  queryRelated(params) {
409
- return query.where(makeWhere(params));
396
+ const obj = {};
397
+ for (let i = 0; i < len; i++) {
398
+ obj[primaryKeys[i]] = params[foreignKeys[i]];
399
+ }
400
+ return query.where(obj);
410
401
  },
411
402
  virtualColumn: new BelongsToVirtualColumn(
412
403
  defaultSchemaConfig,
@@ -420,13 +411,21 @@ const makeBelongsToMethod = (tableConfig, table, relation, relationName, query)
420
411
  reverseJoin
421
412
  };
422
413
  };
423
- const nestedInsert$3 = ({ query, primaryKeys }) => {
414
+ const nestedInsert$3 = ({ query, primaryKeys, on }) => {
424
415
  return async (_, data) => {
425
416
  const t = query.clone();
426
417
  const items = [];
427
418
  for (const item of data) {
428
419
  if (item.connectOrCreate) {
429
- items.push(item);
420
+ items.push(
421
+ on ? {
422
+ ...item,
423
+ connectOrCreate: {
424
+ ...item.connectOrCreate,
425
+ where: { ...item.connectOrCreate.where, ...on }
426
+ }
427
+ } : item
428
+ );
430
429
  }
431
430
  }
432
431
  let connectOrCreated;
@@ -464,7 +463,9 @@ const nestedInsert$3 = ({ query, primaryKeys }) => {
464
463
  items.length = 0;
465
464
  for (const item of data) {
466
465
  if (item.connect) {
467
- items.push(item);
466
+ items.push(
467
+ on ? { ...item, connect: { ...item.connect, ...on } } : item
468
+ );
468
469
  }
469
470
  }
470
471
  let connected;
@@ -551,12 +552,11 @@ const nestedUpdate$3 = ({ query, primaryKeys, foreignKeys, len }) => {
551
552
  }
552
553
  obj[primaryKeys[i]] = id;
553
554
  }
554
- if (obj) {
555
- await _queryUpdate(
556
- query.findBy(obj),
557
- upsert.update
558
- );
559
- } else {
555
+ const count = obj ? await _queryUpdate(
556
+ query.findBy(obj),
557
+ upsert.update
558
+ ) : 0;
559
+ if (!count) {
560
560
  const data = typeof upsert.create === "function" ? upsert.create() : upsert.create;
561
561
  const result = await _queryCreate(
562
562
  query.select(...primaryKeys),
@@ -647,10 +647,13 @@ const makeHasOneMethod = (tableConfig, table, relation, relationName, query) =>
647
647
  const { through, source } = relation.options;
648
648
  const throughRelation = getThroughRelation(table, through);
649
649
  const sourceRelation = getSourceRelation(throughRelation, source);
650
+ const sourceRelationQuery = sourceRelation.query.as(
651
+ relationName
652
+ );
650
653
  const sourceQuery = sourceRelation.joinQuery(
651
- sourceRelation.query,
654
+ sourceRelationQuery,
652
655
  throughRelation.query
653
- ).as(relationName);
656
+ );
654
657
  const whereExistsCallback = () => sourceQuery;
655
658
  const reverseJoin2 = (baseQuery, joiningQuery) => {
656
659
  return joinHasThrough(
@@ -682,6 +685,11 @@ const makeHasOneMethod = (tableConfig, table, relation, relationName, query) =>
682
685
  }
683
686
  const primaryKeys = relation.options.columns;
684
687
  const foreignKeys = relation.options.references;
688
+ const { on } = relation.options;
689
+ if (on) {
690
+ _queryWhere(query, [on]);
691
+ _queryDefaults(query, on);
692
+ }
685
693
  addAutoForeignKey(
686
694
  tableConfig,
687
695
  query,
@@ -690,7 +698,7 @@ const makeHasOneMethod = (tableConfig, table, relation, relationName, query) =>
690
698
  foreignKeys,
691
699
  relation.options
692
700
  );
693
- const state = { query, primaryKeys, foreignKeys };
701
+ const state = { query, primaryKeys, foreignKeys, on };
694
702
  const len = primaryKeys.length;
695
703
  const reversedOn = {};
696
704
  for (let i = 0; i < len; i++) {
@@ -713,7 +721,7 @@ const makeHasOneMethod = (tableConfig, table, relation, relationName, query) =>
713
721
  for (let i = 0; i < len; i++) {
714
722
  values[foreignKeys[i]] = params[primaryKeys[i]];
715
723
  }
716
- return _queryDefaults(query.where(values), values);
724
+ return _queryDefaults(query.where(values), { ...on, ...values });
717
725
  },
718
726
  virtualColumn: new HasOneVirtualColumn(
719
727
  defaultSchemaConfig,
@@ -837,7 +845,9 @@ const nestedUpdate$2 = ({ query, primaryKeys, foreignKeys }) => {
837
845
  } else if (params.update) {
838
846
  await _queryUpdate(currentRelationsQuery, params.update);
839
847
  } else if (params.delete) {
840
- await _queryDelete(currentRelationsQuery);
848
+ const q = _queryDelete(currentRelationsQuery);
849
+ q.q.returnType = "value";
850
+ await q;
841
851
  } else if (params.upsert) {
842
852
  const { update, create } = params.upsert;
843
853
  currentRelationsQuery.q.select = foreignKeys;
@@ -946,6 +956,11 @@ const makeHasManyMethod = (tableConfig, table, relation, relationName, query) =>
946
956
  }
947
957
  const primaryKeys = relation.options.columns;
948
958
  const foreignKeys = relation.options.references;
959
+ const { on } = relation.options;
960
+ if (on) {
961
+ _queryWhere(query, [on]);
962
+ _queryDefaults(query, on);
963
+ }
949
964
  addAutoForeignKey(
950
965
  tableConfig,
951
966
  query,
@@ -954,7 +969,7 @@ const makeHasManyMethod = (tableConfig, table, relation, relationName, query) =>
954
969
  foreignKeys,
955
970
  relation.options
956
971
  );
957
- const state = { query, primaryKeys, foreignKeys };
972
+ const state = { query, primaryKeys, foreignKeys, on };
958
973
  const len = primaryKeys.length;
959
974
  const reversedOn = {};
960
975
  for (let i = 0; i < len; i++) {
@@ -977,7 +992,7 @@ const makeHasManyMethod = (tableConfig, table, relation, relationName, query) =>
977
992
  for (let i = 0; i < len; i++) {
978
993
  values[foreignKeys[i]] = params[primaryKeys[i]];
979
994
  }
980
- return _queryDefaults(query.where(values), values);
995
+ return _queryDefaults(query.where(values), { ...on, ...values });
981
996
  },
982
997
  virtualColumn: new HasManyVirtualColumn(
983
998
  defaultSchemaConfig,
@@ -1033,7 +1048,7 @@ const nestedInsert$1 = ({ query, primaryKeys, foreignKeys }) => {
1033
1048
  obj[foreignKeys[i2]] = selfData[primaryKeys[i2]];
1034
1049
  }
1035
1050
  items[i] = _queryUpdateOrThrow(
1036
- t.orWhere(...connect),
1051
+ t.where({ OR: connect }),
1037
1052
  obj
1038
1053
  );
1039
1054
  }
@@ -1131,6 +1146,29 @@ const nestedUpdate$1 = ({ query, primaryKeys, foreignKeys }) => {
1131
1146
  );
1132
1147
  delete t.q.sqlCache;
1133
1148
  }
1149
+ if (params.add) {
1150
+ if (data.length > 1) {
1151
+ throw new OrchidOrmInternalError(
1152
+ query,
1153
+ "`connect` is not available when updating multiple records, it is only applicable for a single record update"
1154
+ );
1155
+ }
1156
+ const obj = {};
1157
+ for (let i = 0; i < len; i++) {
1158
+ obj[foreignKeys[i]] = data[0][primaryKeys[i]];
1159
+ }
1160
+ const relatedWheres = toArray(params.add);
1161
+ const count = await _queryUpdate(
1162
+ t.where({ OR: relatedWheres }),
1163
+ obj
1164
+ );
1165
+ if (count < relatedWheres.length) {
1166
+ throw new OrchidOrmInternalError(
1167
+ query,
1168
+ `Expected to find at least ${relatedWheres.length} record(s) based on \`add\` conditions, but found ${count}`
1169
+ );
1170
+ }
1171
+ }
1134
1172
  if (params.disconnect || params.set) {
1135
1173
  const obj = {};
1136
1174
  for (const foreignKey of foreignKeys) {
@@ -1223,6 +1261,11 @@ const makeHasAndBelongsToManyMethod = (tableConfig, table, qb, relation, relatio
1223
1261
  const joinTable = options.through.table;
1224
1262
  const throughForeignKeys = options.through.columns;
1225
1263
  const throughPrimaryKeys = options.through.references;
1264
+ const { on } = options;
1265
+ if (on) {
1266
+ _queryWhere(query, [on]);
1267
+ _queryDefaults(query, on);
1268
+ }
1226
1269
  const { snakeCase } = table.internal;
1227
1270
  const foreignKeysFull = foreignKeys.map((key, i) => {
1228
1271
  if (snakeCase) key = foreignKeys[i] = toSnakeCase(key);
@@ -1242,10 +1285,11 @@ const makeHasAndBelongsToManyMethod = (tableConfig, table, qb, relation, relatio
1242
1285
  baseQuery.baseQuery = baseQuery;
1243
1286
  baseQuery.table = joinTable;
1244
1287
  const shape = {};
1288
+ const primaryKeysShape = {};
1245
1289
  for (let i = 0; i < len; i++) {
1246
- shape[foreignKeys[i]] = removeColumnName(
1247
- table.shape[primaryKeys[i]]
1248
- );
1290
+ const pk = primaryKeys[i];
1291
+ shape[foreignKeys[i]] = removeColumnName(table.shape[pk]);
1292
+ primaryKeysShape[pk] = table.shape[pk];
1249
1293
  }
1250
1294
  for (let i = 0; i < throughLen; i++) {
1251
1295
  shape[throughForeignKeys[i]] = removeColumnName(
@@ -1283,7 +1327,9 @@ const makeHasAndBelongsToManyMethod = (tableConfig, table, qb, relation, relatio
1283
1327
  throughPrimaryKeys,
1284
1328
  foreignKeysFull,
1285
1329
  throughForeignKeysFull,
1286
- throughPrimaryKeysFull
1330
+ throughPrimaryKeysFull,
1331
+ primaryKeysShape,
1332
+ on
1287
1333
  };
1288
1334
  const joinQuery = (joiningQuery, tableAs, foreignAs, joinedShapes) => {
1289
1335
  const cloned = joiningQuery.clone();
@@ -1323,20 +1369,21 @@ const makeHasAndBelongsToManyMethod = (tableConfig, table, qb, relation, relatio
1323
1369
  return {
1324
1370
  returns: "many",
1325
1371
  queryRelated(params) {
1326
- return query.whereExists(subQuery, (q) => {
1327
- q = q.clone();
1372
+ const q = query.whereExists(subQuery, (q2) => {
1373
+ q2 = q2.clone();
1328
1374
  const where = {};
1329
1375
  for (let i = 0; i < len; i++) {
1330
1376
  where[foreignKeysFull[i]] = params[primaryKeys[i]];
1331
1377
  }
1332
1378
  for (let i = 0; i < throughLen; i++) {
1333
- _queryJoinOn(q, [
1379
+ _queryJoinOn(q2, [
1334
1380
  throughForeignKeysFull[i],
1335
1381
  throughPrimaryKeysFull[i]
1336
1382
  ]);
1337
1383
  }
1338
- return _queryWhere(q, [where]);
1384
+ return _queryWhere(q2, [where]);
1339
1385
  });
1386
+ return on ? _queryDefaults(q, on) : q;
1340
1387
  },
1341
1388
  virtualColumn: new HasAndBelongsToManyVirtualColumn(
1342
1389
  subQuery,
@@ -1411,6 +1458,19 @@ const queryJoinTable = (state, data, conditions) => {
1411
1458
  }
1412
1459
  ]);
1413
1460
  }
1461
+ if (state.on) {
1462
+ _queryWhereExists(t, state.relatedTableQuery, [
1463
+ (q) => {
1464
+ for (let i = 0; i < state.throughPrimaryKeys.length; i++) {
1465
+ _queryJoinOn(q, [
1466
+ state.throughPrimaryKeysFull[i],
1467
+ state.throughForeignKeysFull[i]
1468
+ ]);
1469
+ }
1470
+ return q;
1471
+ }
1472
+ ]);
1473
+ }
1414
1474
  return t;
1415
1475
  };
1416
1476
  const conditionsToWhereArg = (conditions) => Array.isArray(conditions) ? { OR: conditions } : conditions;
@@ -1580,7 +1640,7 @@ const nestedInsert = ({
1580
1640
  const nestedUpdate = (state) => {
1581
1641
  const len = state.primaryKeys.length;
1582
1642
  const throughLen = state.throughPrimaryKeys.length;
1583
- return async (_, data, params) => {
1643
+ return async (query, data, params) => {
1584
1644
  if (params.create) {
1585
1645
  const idsRows = await _queryCreateMany(
1586
1646
  _queryRows(state.relatedTableQuery.select(...state.throughPrimaryKeys)),
@@ -1628,6 +1688,58 @@ const nestedUpdate = (state) => {
1628
1688
  params.update.data
1629
1689
  );
1630
1690
  }
1691
+ if (params.add) {
1692
+ const as = query.table;
1693
+ const relatedWheres = toArray(params.add);
1694
+ const joinTableColumns = [
1695
+ ...state.foreignKeys,
1696
+ ...state.throughForeignKeys
1697
+ ];
1698
+ try {
1699
+ const count = await state.joinTableQuery.insertManyFrom(
1700
+ _querySelect(
1701
+ state.relatedTableQuery.whereOneOf(...relatedWheres),
1702
+ [
1703
+ Object.fromEntries([
1704
+ ...state.primaryKeys.map((key, i) => [
1705
+ state.foreignKeys[i],
1706
+ as + "." + (state.primaryKeysShape[key].data.name || key)
1707
+ ]),
1708
+ ...state.throughForeignKeys.map((key, i) => [
1709
+ key,
1710
+ state.throughPrimaryKeys[i]
1711
+ ])
1712
+ ])
1713
+ ]
1714
+ ).joinData(
1715
+ as,
1716
+ () => Object.fromEntries(
1717
+ state.primaryKeys.map((key) => [
1718
+ key,
1719
+ state.primaryKeysShape[key]
1720
+ ])
1721
+ ),
1722
+ data.map((x) => pick(x, state.primaryKeys))
1723
+ )
1724
+ ).onConflict(joinTableColumns).merge([state.foreignKeys[0]]);
1725
+ if (count < data.length * relatedWheres.length) {
1726
+ throw new OrchidOrmInternalError(
1727
+ query,
1728
+ `Expected to find at least ${relatedWheres.length} record(s) based on \`add\` conditions, but found ${count / data.length}`
1729
+ );
1730
+ }
1731
+ } catch (err) {
1732
+ if (err.code === "42P10") {
1733
+ throw new OrchidOrmInternalError(
1734
+ query,
1735
+ `"${state.joinTableQuery.table}" must have a primary key or a unique index on columns (${joinTableColumns.join(
1736
+ ", "
1737
+ )}) for this kind of query.`
1738
+ );
1739
+ }
1740
+ throw err;
1741
+ }
1742
+ }
1631
1743
  if (params.disconnect) {
1632
1744
  await _queryDelete(
1633
1745
  queryJoinTable(state, data, params.disconnect)
@@ -1755,7 +1867,7 @@ const delayRelation = (delayedRelations, table, relationName, data) => {
1755
1867
  const applyRelation = (table, qb, { relationName, relation, dbTable, otherDbTable }, delayedRelations) => {
1756
1868
  const baseQuery = Object.create(otherDbTable);
1757
1869
  baseQuery.baseQuery = baseQuery;
1758
- const query = (relation.options.scope ? relation.options.scope(baseQuery) : baseQuery).as(relationName);
1870
+ const query = baseQuery.as(relationName);
1759
1871
  const definedAs = query.definedAs;
1760
1872
  if (!definedAs) {
1761
1873
  throw new Error(