orchid-orm 1.72.4 → 1.72.6

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.d.ts CHANGED
@@ -96,6 +96,11 @@ interface HasManyInfo<T extends RelationConfigSelf, Name extends string, Rel ext
96
96
  set?: MaybeArray<WhereArg<Q>>;
97
97
  add?: MaybeArray<WhereArg<Q>>;
98
98
  create?: CreateData<Q>[];
99
+ upsert?: {
100
+ findBy: Q['internal']['uniqueColumns'];
101
+ update: UpdateData<Q>;
102
+ create?: CreateData<Q> | (() => CreateData<Q>);
103
+ };
99
104
  } : never;
100
105
  }
101
106
  interface RelJoin extends JoinQueryMethod {
@@ -428,6 +433,11 @@ interface HasAndBelongsToManyInfo<T extends RelationConfigSelf, Name extends str
428
433
  data: UpdateData<Q>;
429
434
  };
430
435
  create?: CreateData<Q>[];
436
+ upsert?: {
437
+ findBy: Q['internal']['uniqueColumns'];
438
+ update: UpdateData<Q>;
439
+ create?: CreateData<Q> | (() => CreateData<Q>);
440
+ };
431
441
  } : {
432
442
  disconnect?: MaybeArray<WhereArg<Q>>;
433
443
  set?: MaybeArray<WhereArg<Q>>;
package/dist/index.js CHANGED
@@ -137,6 +137,25 @@ function createBaseTable({ schemaConfig = pqb_internal.defaultSchemaConfig, colu
137
137
  base.prototype.autoForeignKeys = autoForeignKeys === true ? {} : autoForeignKeys || void 0;
138
138
  return base;
139
139
  }
140
+ const throwIfQueryReturnsAllForNestedUpdate = (q, params) => {
141
+ if (!(params.set || params.create || params.upsert) || !(0, pqb_internal.isQueryReturnsAll)(q)) return;
142
+ let key;
143
+ if (params.set) key = "set";
144
+ else if (params.create) key = "create";
145
+ else key = "upsert";
146
+ throw new Error(`\`${key}\` option is not allowed in a batch update`);
147
+ };
148
+ const getNestedUpdateUpsertCreate = (upsert) => {
149
+ if (typeof upsert.create === "function") return upsert.create();
150
+ return upsert.create || pqb_internal.emptyObject;
151
+ };
152
+ const makeNestedUpdateUpsertData = (upsert, setIds) => ({
153
+ update: upsert.update,
154
+ create: {
155
+ ...getNestedUpdateUpsertCreate(upsert),
156
+ ...setIds
157
+ }
158
+ });
140
159
  const getThroughRelation = (table, through) => {
141
160
  return table.relations[through];
142
161
  };
@@ -224,8 +243,27 @@ const getColumnKeyFromDbName = (query, name) => {
224
243
  for (const k in query.shape) if (query.shape[k].data.name === name) return k;
225
244
  return name;
226
245
  };
227
- const selectCteColumnsSql = (cteAs, columns) => `(SELECT ${columns.map((c) => `"${cteAs}"."${c}"`).join(", ")} FROM "${cteAs}")`;
228
- const selectCteColumnSql = (cteAs, column) => `(SELECT "${cteAs}"."${column}" FROM "${cteAs}")`;
246
+ const selectCteColumnsSql$1 = (cteAs, columns) => `(SELECT ${columns.map((c) => `"${cteAs}"."${c}"`).join(", ")} FROM "${cteAs}")`;
247
+ const selectCteColumnSql$1 = (cteAs, column) => `(SELECT "${cteAs}"."${column}" FROM "${cteAs}")`;
248
+ const makeNestedUpdateRelationIds = (q, relQuery, primaryKeys, foreignKeys) => {
249
+ const selectIdsSql = new pqb_internal.RawSql("");
250
+ const setIds = {};
251
+ for (const foreignKey of foreignKeys) setIds[foreignKey] = new pqb_internal.RawSql("");
252
+ let appendedAs;
253
+ (0, pqb_internal._hookSelectColumns)(q, primaryKeys, (aliasedPrimaryKeys) => {
254
+ selectIdsSql._sql = selectCteColumnsSql$1(appendedAs, aliasedPrimaryKeys);
255
+ foreignKeys.forEach((foreignKey, i) => {
256
+ setIds[foreignKey]._sql = selectCteColumnSql$1(appendedAs, aliasedPrimaryKeys[i]);
257
+ });
258
+ });
259
+ return {
260
+ existingRelQuery: (0, pqb_internal._queryWhereIn)((0, pqb_internal._clone)(relQuery), true, foreignKeys, selectIdsSql),
261
+ setIds,
262
+ setAppendedAs(as) {
263
+ appendedAs = as;
264
+ }
265
+ };
266
+ };
229
267
  const selectCteColumnFromManySql = (cteAs, column, rowIndex, count) => {
230
268
  let sql = `(SELECT "${cteAs}"."${column}" FROM "${cteAs}"`;
231
269
  if (count > 1) {
@@ -406,7 +444,7 @@ const nestedUpdate$2 = ({ query, primaryKeys, foreignKeys, len }) => {
406
444
  } else if (params.update) {
407
445
  let appendedAs;
408
446
  (0, pqb_internal._hookSelectColumns)(self, foreignKeys, (aliasedForeignKeys) => {
409
- selectIdsSql._sql = selectCteColumnsSql(appendedAs, aliasedForeignKeys);
447
+ selectIdsSql._sql = selectCteColumnsSql$1(appendedAs, aliasedForeignKeys);
410
448
  });
411
449
  const selectIdsSql = new pqb_internal.RawSql("");
412
450
  const updateQuery = (0, pqb_internal._queryUpdate)((0, pqb_internal._queryWhereIn)(query.clone(), true, primaryKeys, selectIdsSql), params.update);
@@ -449,7 +487,7 @@ const relWithSelectIds = (self, rel, primaryKeys, foreignKeys) => {
449
487
  const selectIdsQuery = makeSelectIdsQuery(self, foreignKeys);
450
488
  const selectIdsSql = new pqb_internal.RawSql("");
451
489
  (0, pqb_internal._prependWith)(self, (as) => {
452
- selectIdsSql._sql = selectCteColumnsSql(as, foreignKeys);
490
+ selectIdsSql._sql = selectCteColumnsSql$1(as, foreignKeys);
453
491
  }, selectIdsQuery);
454
492
  return {
455
493
  selectIdsSql,
@@ -470,12 +508,12 @@ const setForeignKeysFromCte = (record, primaryKeys, foreignKeys, mustExist) => {
470
508
  foreignKeys.forEach(mustExist ? (foreignKey, i) => {
471
509
  record[foreignKey]._sql = selectCteColumnMustExistSql(i, as, primaryKeys[i]);
472
510
  } : (foreignKey, i) => {
473
- record[foreignKey]._sql = selectCteColumnSql(as, primaryKeys[i]);
511
+ record[foreignKey]._sql = selectCteColumnSql$1(as, primaryKeys[i]);
474
512
  });
475
513
  };
476
514
  };
477
515
  const selectCteColumnMustExistSql = (i, cteAs, column) => {
478
- const selectColumn = selectCteColumnSql(cteAs, column);
516
+ const selectColumn = selectCteColumnSql$1(cteAs, column);
479
517
  return i === 0 ? `CASE WHEN (SELECT count(*) FROM "${cteAs}") = 0 AND (SELECT 'not-found')::int = 0 THEN NULL ELSE ${selectColumn} END` : selectColumn;
480
518
  };
481
519
  var HasOneVirtualColumn = class extends pqb_internal.VirtualColumn {
@@ -541,43 +579,19 @@ var HasOneVirtualColumn = class extends pqb_internal.VirtualColumn {
541
579
  update(self, set) {
542
580
  const querySelf = self;
543
581
  const params = set[this.key];
544
- if ((params.set || params.create || params.upsert) && (0, pqb_internal.isQueryReturnsAll)(querySelf)) {
545
- const key = params.set ? "set" : params.create ? "create" : "upsert";
546
- throw new Error(`\`${key}\` option is not allowed in a batch update`);
547
- }
582
+ throwIfQueryReturnsAllForNestedUpdate(querySelf, params);
548
583
  const { primaryKeys, foreignKeys, query: relQuery } = this.state;
549
584
  if (params.create || params.update || params.upsert || params.disconnect || params.set || params.delete) {
550
- let appendedAs;
551
- (0, pqb_internal._hookSelectColumns)(querySelf, primaryKeys, (aliasedPrimaryKeys) => {
552
- selectIdsSql._sql = selectCteColumnsSql(appendedAs, aliasedPrimaryKeys);
553
- if (params.create || params.set || params.upsert) foreignKeys.forEach((foreignKey, i) => {
554
- setIds[foreignKey]._sql = selectCteColumnSql(appendedAs, aliasedPrimaryKeys[i]);
555
- });
556
- });
557
- const selectIdsSql = new pqb_internal.RawSql("");
558
- const existingRelQuery = (0, pqb_internal._queryWhereIn)((0, pqb_internal._clone)(relQuery), true, foreignKeys, selectIdsSql);
559
- let setIds = void 0;
560
- if (params.create || params.set || params.upsert) {
561
- setIds = {};
562
- foreignKeys.forEach((foreignKey) => {
563
- setIds[foreignKey] = new pqb_internal.RawSql("");
564
- });
565
- }
566
- const nullifyOrDeleteQuery = params.update ? (0, pqb_internal._queryUpdate)(existingRelQuery, params.update) : params.upsert ? (0, pqb_internal._queryUpsert)(existingRelQuery, {
567
- update: params.upsert.update,
568
- create: {
569
- ...typeof params.upsert.create === "function" ? params.upsert.create() : params.upsert.create,
570
- ...setIds
571
- }
572
- }) : params.delete ? (0, pqb_internal._queryDelete)(existingRelQuery) : (0, pqb_internal._queryUpdate)(existingRelQuery, this.setNulls);
585
+ const ids = makeNestedUpdateRelationIds(querySelf, relQuery, primaryKeys, foreignKeys);
586
+ const nullifyOrDeleteQuery = params.update ? (0, pqb_internal._queryUpdate)(ids.existingRelQuery, params.update) : params.upsert ? (0, pqb_internal._queryUpsert)(ids.existingRelQuery, makeNestedUpdateUpsertData(params.upsert, ids.setIds)) : params.delete ? (0, pqb_internal._queryDelete)(ids.existingRelQuery) : (0, pqb_internal._queryUpdate)(ids.existingRelQuery, this.setNulls);
573
587
  nullifyOrDeleteQuery.q.returnType = "void";
574
- (0, pqb_internal._appendQuery)(querySelf, nullifyOrDeleteQuery, (as) => appendedAs = as);
588
+ (0, pqb_internal._appendQuery)(querySelf, nullifyOrDeleteQuery, ids.setAppendedAs);
575
589
  if (params.create) (0, pqb_internal._appendQuery)(querySelf, (0, pqb_internal._queryInsert)((0, pqb_internal._clone)(relQuery), {
576
590
  ...params.create,
577
- ...setIds
591
+ ...ids.setIds
578
592
  }), pqb_internal.noop);
579
593
  else if (params.set) {
580
- const setQuery = (0, pqb_internal._queryUpdate)((0, pqb_internal._queryWhere)((0, pqb_internal._clone)(relQuery), [params.set]), setIds);
594
+ const setQuery = (0, pqb_internal._queryUpdate)((0, pqb_internal._queryWhere)((0, pqb_internal._clone)(relQuery), [params.set]), ids.setIds);
581
595
  setQuery.q.returnType = "void";
582
596
  (0, pqb_internal._appendQuery)(querySelf, setQuery, pqb_internal.noop);
583
597
  }
@@ -691,6 +705,8 @@ var HasManyVirtualColumn = class extends pqb_internal.VirtualColumn {
691
705
  this.state = state;
692
706
  this.nestedInsert = nestedInsert$1(state);
693
707
  this.nestedUpdate = nestedUpdate$1(state);
708
+ this.setNulls = {};
709
+ for (const foreignKey of state.foreignKeys) this.setNulls[foreignKey] = null;
694
710
  }
695
711
  create(self, ctx, items, rowIndexes, count) {
696
712
  const querySelf = self;
@@ -761,14 +777,18 @@ var HasManyVirtualColumn = class extends pqb_internal.VirtualColumn {
761
777
  });
762
778
  } else hasRelationHandleCreate(querySelf, ctx, items, rowIndexes, this.key, this.state.primaryKeys, this.nestedInsert);
763
779
  }
764
- update(q, set) {
765
- const query = q;
780
+ update(self, set) {
781
+ const querySelf = self;
766
782
  const params = set[this.key];
767
- if ((params.set || params.create) && (0, pqb_internal.isQueryReturnsAll)(query)) {
768
- const key = params.set ? "set" : "create";
769
- throw new Error(`\`${key}\` option is not allowed in a batch update`);
783
+ throwIfQueryReturnsAllForNestedUpdate(querySelf, params);
784
+ hasRelationHandleUpdate(querySelf, set, this.key, this.state.primaryKeys, this.nestedUpdate);
785
+ if (params.upsert) {
786
+ const { primaryKeys, foreignKeys, query: relQuery } = this.state;
787
+ const ids = makeNestedUpdateRelationIds(querySelf, relQuery, primaryKeys, foreignKeys);
788
+ const appendedQuery = (0, pqb_internal._queryUpsert)(ids.existingRelQuery, makeNestedUpdateUpsertData(params.upsert, ids.setIds));
789
+ appendedQuery.q.returnType = "void";
790
+ (0, pqb_internal._appendQuery)(querySelf, appendedQuery, ids.setAppendedAs);
770
791
  }
771
- hasRelationHandleUpdate(query, set, this.key, this.state.primaryKeys, this.nestedUpdate);
772
792
  }
773
793
  };
774
794
  const makeHasManyMethod = (tableConfig, table, relation, relationName, query) => {
@@ -908,10 +928,10 @@ const nestedInsert$1 = ({ query, primaryKeys, foreignKeys }) => {
908
928
  }
909
929
  });
910
930
  };
911
- const nestedUpdate$1 = ({ query, primaryKeys, foreignKeys }) => {
931
+ const nestedUpdate$1 = ({ query: relQuery, primaryKeys, foreignKeys }) => {
912
932
  const len = primaryKeys.length;
913
933
  return (async (_, data, params) => {
914
- const t = query.clone();
934
+ const t = relQuery.clone();
915
935
  if (params.create) {
916
936
  const obj = {};
917
937
  for (let i = 0; i < len; i++) obj[foreignKeys[i]] = data[0][primaryKeys[i]];
@@ -921,12 +941,12 @@ const nestedUpdate$1 = ({ query, primaryKeys, foreignKeys }) => {
921
941
  })));
922
942
  }
923
943
  if (params.add) {
924
- if (data.length > 1) throw new pqb.OrchidOrmInternalError(query, "`connect` is not available when updating multiple records, it is only applicable for a single record update");
944
+ if (data.length > 1) throw new pqb.OrchidOrmInternalError(relQuery, "`connect` is not available when updating multiple records, it is only applicable for a single record update");
925
945
  const obj = {};
926
946
  for (let i = 0; i < len; i++) obj[foreignKeys[i]] = data[0][primaryKeys[i]];
927
947
  const relatedWheres = (0, pqb_internal.toArray)(params.add);
928
948
  const count = await (0, pqb_internal._queryUpdate)(t.where({ OR: relatedWheres }), obj);
929
- if (count < relatedWheres.length) throw new pqb.OrchidOrmInternalError(query, `Expected to find at least ${relatedWheres.length} record(s) based on \`add\` conditions, but found ${count}`);
949
+ if (count < relatedWheres.length) throw new pqb.OrchidOrmInternalError(relQuery, `Expected to find at least ${relatedWheres.length} record(s) based on \`add\` conditions, but found ${count}`);
930
950
  }
931
951
  if (params.disconnect || params.set) {
932
952
  const obj = {};
@@ -961,9 +981,48 @@ var HasAndBelongsToManyVirtualColumn = class extends pqb_internal.VirtualColumn
961
981
  hasRelationHandleCreate(q, ctx, items, rowIndexes, this.key, this.state.primaryKeys, this.nestedInsert);
962
982
  }
963
983
  update(q, set) {
964
- hasRelationHandleUpdate(q, set, this.key, this.state.primaryKeys, this.nestedUpdate);
984
+ const querySelf = q;
985
+ const params = set[this.key];
986
+ hasRelationHandleUpdate(querySelf, set, this.key, this.state.primaryKeys, this.nestedUpdate);
987
+ if (params.upsert) {
988
+ throwIfQueryReturnsAllForNestedUpdate(querySelf, { upsert: params.upsert });
989
+ nestedUpdateUpsert(querySelf, this.state, params.upsert);
990
+ }
965
991
  }
966
992
  };
993
+ const selectCteColumnsSql = (cteAs, columns) => `(SELECT ${columns.map((c) => `"${cteAs}"."${c}"`).join(", ")} FROM "${cteAs}")`;
994
+ const selectCteColumnSql = (cteAs, column) => `(SELECT "${cteAs}"."${column}" FROM "${cteAs}")`;
995
+ const nestedUpdateUpsert = (querySelf, state, upsert) => {
996
+ const parentIdsSql = new pqb_internal.RawSql("");
997
+ const parentValues = state.foreignKeys.map(() => new pqb_internal.RawSql(""));
998
+ const relatedIdsSql = new pqb_internal.RawSql("");
999
+ const relatedValues = state.throughPrimaryKeys.map(() => new pqb_internal.RawSql(""));
1000
+ let parentAs;
1001
+ (0, pqb_internal._hookSelectColumns)(querySelf, state.primaryKeys, (aliasedPrimaryKeys) => {
1002
+ parentIdsSql._sql = selectCteColumnsSql(parentAs, aliasedPrimaryKeys);
1003
+ for (let i = 0; i < state.foreignKeys.length; i++) parentValues[i]._sql = selectCteColumnSql(parentAs, aliasedPrimaryKeys[i]);
1004
+ });
1005
+ const upsertQuery = (0, pqb_internal._querySelect)((0, pqb_internal._queryUpsert)((0, pqb_internal._queryWhereExists)((0, pqb_internal._queryWhere)((0, pqb_internal._clone)(state.relatedTableQuery), [upsert.findBy]), state.joinTableQuery, [(q) => {
1006
+ for (let i = 0; i < state.throughPrimaryKeys.length; i++) (0, pqb_internal._queryJoinOn)(q, [state.throughForeignKeysFull[i], state.throughPrimaryKeysFull[i]]);
1007
+ return (0, pqb_internal._queryWhereIn)(q, true, state.foreignKeysFull, parentIdsSql);
1008
+ }]), {
1009
+ update: upsert.update,
1010
+ create: upsert.create || {}
1011
+ }), state.throughPrimaryKeys);
1012
+ const selectAs = {};
1013
+ for (let i = 0; i < state.foreignKeys.length; i++) selectAs[state.foreignKeys[i]] = parentValues[i];
1014
+ for (let i = 0; i < state.throughForeignKeys.length; i++) selectAs[state.throughForeignKeys[i]] = relatedValues[i];
1015
+ const joinRows = (0, pqb_internal._querySelect)((0, pqb_internal._clone)(state.queryBuilder), [selectAs]);
1016
+ (0, pqb_internal._queryWhere)(joinRows, [relatedIdsSql]);
1017
+ const joinQuery = state.joinTableQuery.insertForEachFrom(joinRows);
1018
+ joinQuery.q.returnType = "void";
1019
+ (0, pqb_internal._appendQuery)(querySelf, (0, pqb_internal._appendQueryOnUpsertCreate)(upsertQuery, joinQuery, (as) => {
1020
+ relatedIdsSql._sql = `EXISTS (SELECT 1 FROM "${as}")`;
1021
+ for (let i = 0; i < relatedValues.length; i++) relatedValues[i]._sql = selectCteColumnSql(as, state.throughPrimaryKeys[i]);
1022
+ }), (as) => {
1023
+ parentAs = as;
1024
+ });
1025
+ };
967
1026
  const removeColumnName = (column) => {
968
1027
  if (!column.data.name) return column;
969
1028
  const cloned = Object.create(column);
@@ -1021,6 +1080,7 @@ const makeHasAndBelongsToManyMethod = (tableConfig, table, qb, relation, relatio
1021
1080
  addAutoForeignKey(tableConfig, subQuery, table, primaryKeys, foreignKeys, relation.options, originalForeignKeys);
1022
1081
  addAutoForeignKey(tableConfig, subQuery, query, throughPrimaryKeys, throughForeignKeys, relation.options.through, originalThroughForeignKeys);
1023
1082
  const state = {
1083
+ queryBuilder: qb,
1024
1084
  relatedTableQuery: query,
1025
1085
  joinTableQuery: subQuery,
1026
1086
  primaryKeys,