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.d.ts CHANGED
@@ -1,16 +1,14 @@
1
- import { Query, TableData, SelectableFromShape, CreateMethodsNames, CreateData, CreateBelongsToData, AddQueryDefaults, RelationConfigBase, SetQueryReturnsOne, SetQueryReturnsOneOptional, UpdateData, WhereArg, JoinQueryMethod, DeleteMethodsNames, Db, IsolationLevel, TransactionOptions, Adapter, FromArg, FromResult, AdapterOptions, DbSharedOptions, RelationsBase, ShapeColumnPrimaryKeys, ShapeUniqueColumns, TableDataItemsUniqueColumns, TableDataItemsUniqueColumnTuples, UniqueConstraints, TableDataItemsUniqueConstraints, ComputedColumnsFromOptions, MapTableScopesOption, TableDataItem, ComputedOptionsFactory, QueryData, TableDataFn, DbTableOptionScopes, RawSQL, DynamicRawSQL, DefaultSchemaConfig, DefaultColumnTypes, QueryBeforeHook, QueryAfterHook, AfterHook, WhereResult, MergeQuery } from 'pqb';
1
+ import { TableData, Query, SelectableFromShape, CreateMethodsNames, CreateData, CreateBelongsToData, AddQueryDefaults, RelationConfigBase, SetQueryReturnsOne, SetQueryReturnsOneOptional, UpdateData, WhereArg, JoinQueryMethod, DeleteMethodsNames, Db, IsolationLevel, TransactionOptions, Adapter, FromArg, FromResult, AdapterOptions, DbSharedOptions, RelationsBase, ShapeColumnPrimaryKeys, ShapeUniqueColumns, TableDataItemsUniqueColumns, TableDataItemsUniqueColumnTuples, UniqueConstraints, TableDataItemsUniqueConstraints, ComputedColumnsFromOptions, MapTableScopesOption, TableDataItem, ComputedOptionsFactory, QueryData, TableDataFn, DbTableOptionScopes, RawSQL, DynamicRawSQL, DefaultSchemaConfig, DefaultColumnTypes, QueryBeforeHook, QueryAfterHook, AfterHook, WhereResult, MergeQuery } from 'pqb';
2
2
  export * from 'pqb';
3
- import { ColumnsShapeBase, EmptyObject, MaybeArray, RecordUnknown, ShallowSimplify, ColumnShapeOutput, DefaultSelectColumns, ColumnShapeInput, ColumnShapeInputPartial, IsQuery, CoreQueryScopes, ColumnSchemaConfig, StaticSQLArgs, QueryColumn, DynamicSQLArg, QueryColumns, QueryReturnType } from 'orchid-core';
3
+ import { ColumnsShapeBase, ColumnShapeInputPartial, EmptyObject, MaybeArray, RecordUnknown, ShallowSimplify, ColumnShapeOutput, DefaultSelectColumns, ColumnShapeInput, IsQuery, CoreQueryScopes, ColumnSchemaConfig, StaticSQLArgs, QueryColumn, DynamicSQLArg, QueryColumns, QueryReturnType } from 'orchid-core';
4
4
  export * from 'orchid-core';
5
5
 
6
- interface RelationCommonOptions<Related extends TableClass = TableClass, Scope extends Query = Query> {
7
- scope?: ScopeFn<Related, Scope>;
6
+ interface RelationRefsOptions<Column extends PropertyKey = string, Shape extends ColumnsShapeBase = ColumnsShapeBase> {
8
7
  required?: boolean;
9
- }
10
- interface RelationRefsOptions<Column extends PropertyKey = string, Ref extends PropertyKey = string> {
11
8
  columns: Column[];
12
- references: Ref[];
9
+ references: (keyof Shape)[];
13
10
  foreignKey?: boolean | TableData.References.Options;
11
+ on?: ColumnShapeInputPartial<Shape>;
14
12
  }
15
13
  interface RelationThroughOptions<Through extends PropertyKey = string, Source extends PropertyKey = string> {
16
14
  through: Through;
@@ -21,7 +19,10 @@ interface HasOne extends RelationThunkBase {
21
19
  type: 'hasOne';
22
20
  options: HasOneOptions;
23
21
  }
24
- type HasOneOptions<Columns extends ColumnsShapeBase = ColumnsShapeBase, Related extends TableClass = TableClass, Scope extends Query = Query, Through extends string = string, Source extends string = string> = RelationCommonOptions<Related, Scope> & (RelationRefsOptions<keyof Columns, keyof InstanceType<Related>['columns']['shape']> | RelationThroughOptions<Through, Source>);
22
+ interface RelationHasOneThroughOptions<Through extends string, Source extends string> extends RelationThroughOptions<Through, Source> {
23
+ required?: boolean;
24
+ }
25
+ type HasOneOptions<Columns extends ColumnsShapeBase = ColumnsShapeBase, Related extends TableClass = TableClass, Through extends string = string, Source extends string = string> = RelationRefsOptions<keyof Columns, InstanceType<Related>['columns']['shape']> | RelationHasOneThroughOptions<Through, Source>;
25
26
  type HasOneParams<T extends RelationConfigSelf, Relation extends RelationThunkBase> = Relation['options'] extends RelationRefsOptions ? {
26
27
  [Name in Relation['options']['columns'][number]]: T['columns']['shape'][Name]['type'];
27
28
  } : Relation['options'] extends RelationThroughOptions ? RelationConfigParams<T, T['relations'][Relation['options']['through']]> : never;
@@ -114,6 +115,7 @@ interface HasManyInfo<T extends RelationConfigSelf, Name extends string, Rel ext
114
115
  data: UpdateData<Q>;
115
116
  };
116
117
  set?: MaybeArray<WhereArg<Q>>;
118
+ add?: MaybeArray<WhereArg<Q>>;
117
119
  create?: CreateData<T['relations'][Name]['options'] extends RelationThroughOptions ? Q : AddQueryDefaults<Q, HasOnePopulate<T, Name>>>[];
118
120
  };
119
121
  }
@@ -126,8 +128,7 @@ interface BelongsTo extends RelationThunkBase {
126
128
  type: 'belongsTo';
127
129
  options: BelongsToOptions;
128
130
  }
129
- interface BelongsToOptions<Columns extends ColumnsShapeBase = ColumnsShapeBase, Related extends TableClass = TableClass, Scope extends Query = Query> extends RelationCommonOptions<Related, Scope>, RelationRefsOptions<keyof Columns, keyof InstanceType<Related>['columns']['shape']> {
130
- }
131
+ type BelongsToOptions<Columns extends ColumnsShapeBase = ColumnsShapeBase, Related extends TableClass = TableClass> = RelationRefsOptions<keyof Columns, InstanceType<Related>['columns']['shape']>;
131
132
  type BelongsToFKey<Relation extends RelationThunkBase> = Relation['options'] extends RelationRefsOptions ? Relation['options']['columns'][number] : never;
132
133
  type BelongsToParams<T extends RelationConfigSelf, Relation extends BelongsTo> = {
133
134
  [Name in BelongsToFKey<Relation>]: T['columns']['shape'][Name]['type'];
@@ -290,8 +291,7 @@ interface HasAndBelongsToMany extends RelationThunkBase {
290
291
  type: 'hasAndBelongsToMany';
291
292
  options: HasAndBelongsToManyOptions;
292
293
  }
293
- interface HasAndBelongsToManyOptions<Columns extends ColumnsShapeBase = ColumnsShapeBase, Related extends TableClass = TableClass, Scope extends Query = Query> {
294
- scope?: ScopeFn<Related, Scope>;
294
+ interface HasAndBelongsToManyOptions<Columns extends ColumnsShapeBase = ColumnsShapeBase, Related extends TableClass = TableClass> {
295
295
  required?: boolean;
296
296
  columns: (keyof Columns)[];
297
297
  references: string[];
@@ -302,6 +302,7 @@ interface HasAndBelongsToManyOptions<Columns extends ColumnsShapeBase = ColumnsS
302
302
  references: (keyof InstanceType<Related>['columns']['shape'])[];
303
303
  foreignKey?: boolean | TableData.References.Options;
304
304
  };
305
+ on?: ColumnShapeInputPartial<InstanceType<Related>['columns']['shape']>;
305
306
  }
306
307
  type HasAndBelongsToManyParams<T extends RelationConfigSelf, Relation extends HasAndBelongsToMany> = {
307
308
  [Name in Relation['options']['columns'][number]]: T['columns']['shape'][Name]['type'];
@@ -332,6 +333,7 @@ interface HasAndBelongsToManyInfo<T extends RelationConfigSelf, Name extends str
332
333
  dataForUpdate: {
333
334
  disconnect?: MaybeArray<WhereArg<Q>>;
334
335
  set?: MaybeArray<WhereArg<Q>>;
336
+ add?: MaybeArray<WhereArg<Q>>;
335
337
  delete?: MaybeArray<WhereArg<Q>>;
336
338
  update?: {
337
339
  where: MaybeArray<WhereArg<Q>>;
@@ -342,6 +344,7 @@ interface HasAndBelongsToManyInfo<T extends RelationConfigSelf, Name extends str
342
344
  dataForUpdateOne: {
343
345
  disconnect?: MaybeArray<WhereArg<Q>>;
344
346
  set?: MaybeArray<WhereArg<Q>>;
347
+ add?: MaybeArray<WhereArg<Q>>;
345
348
  delete?: MaybeArray<WhereArg<Q>>;
346
349
  update?: {
347
350
  where: MaybeArray<WhereArg<Q>>;
@@ -389,13 +392,13 @@ type RelationToOneDataForCreateSameQuery<Q extends Query> = {
389
392
  interface RelationThunkBase {
390
393
  type: string;
391
394
  fn(): TableClass;
392
- options: RelationCommonOptions;
395
+ options: unknown;
393
396
  }
394
397
  type RelationThunk = BelongsTo | HasOne | HasMany | HasAndBelongsToMany;
395
398
  interface RelationThunks {
396
399
  [K: string]: RelationThunk;
397
400
  }
398
- type RelationScopeOrTable<Relation extends RelationThunkBase> = Relation['options']['scope'] extends (q: Query) => Query ? ReturnType<Relation['options']['scope']> : ORMTableInputToQueryBuilder<InstanceType<ReturnType<Relation['fn']>>>;
401
+ type RelationTableToQuery<Relation extends RelationThunkBase> = ORMTableInputToQueryBuilder<InstanceType<ReturnType<Relation['fn']>>>;
399
402
  interface RelationConfigSelf {
400
403
  columns: {
401
404
  shape: ColumnsShapeBase;
@@ -404,13 +407,13 @@ interface RelationConfigSelf {
404
407
  }
405
408
  type RelationConfigParams<T extends RelationConfigSelf, Relation extends RelationThunk> = Relation extends BelongsTo ? BelongsToParams<T, Relation> : Relation extends HasOne | HasMany ? HasOneParams<T, Relation> : Relation extends HasAndBelongsToMany ? HasAndBelongsToManyParams<T, Relation> : never;
406
409
  type MapRelation<T extends RelationConfigSelf, K extends keyof T['relations'] & string> = T['relations'][K] extends BelongsTo ? {
407
- relationConfig: BelongsToInfo<T, K, T['relations'][K], T['relations'][K]['options']['columns'][number] & string, T['relations'][K]['options']['required'], BelongsToQuery<RelationScopeOrTable<T['relations'][K]>, K>>;
410
+ relationConfig: BelongsToInfo<T, K, T['relations'][K], T['relations'][K]['options']['columns'][number] & string, T['relations'][K]['options']['required'], BelongsToQuery<RelationTableToQuery<T['relations'][K]>, K>>;
408
411
  } : T['relations'][K] extends HasOne ? {
409
- relationConfig: HasOneInfo<T, K, T['relations'][K], HasOneQuery<T, K, RelationScopeOrTable<T['relations'][K]>>>;
412
+ relationConfig: HasOneInfo<T, K, T['relations'][K], HasOneQuery<T, K, RelationTableToQuery<T['relations'][K]>>>;
410
413
  } : T['relations'][K] extends HasMany ? {
411
- relationConfig: HasManyInfo<T, K, T['relations'][K], HasOneQuery<T, K, RelationScopeOrTable<T['relations'][K]>>>;
414
+ relationConfig: HasManyInfo<T, K, T['relations'][K], HasOneQuery<T, K, RelationTableToQuery<T['relations'][K]>>>;
412
415
  } : T['relations'][K] extends HasAndBelongsToMany ? {
413
- relationConfig: HasAndBelongsToManyInfo<T, K, T['relations'][K], HasAndBelongsToManyQuery<K, RelationScopeOrTable<T['relations'][K]>>>;
416
+ relationConfig: HasAndBelongsToManyInfo<T, K, T['relations'][K], HasAndBelongsToManyQuery<K, RelationTableToQuery<T['relations'][K]>>>;
414
417
  } : never;
415
418
  type MapRelations<T> = T extends RelationConfigSelf ? {
416
419
  [K in keyof T['relations'] & string]: MapRelation<T, K>;
@@ -437,7 +440,6 @@ interface TableToDb<T extends ORMTableInput, Relations extends RelationsBase> ex
437
440
  };
438
441
  }
439
442
  type ORMTableInputToQueryBuilder<T extends ORMTableInput> = T extends RelationConfigSelf ? TableToDb<T, MapRelations<T>> : TableToDb<T, EmptyObject>;
440
- type ScopeFn<Related extends TableClass, Scope extends Query> = (q: ORMTableInputToQueryBuilder<InstanceType<Related>>) => Scope;
441
443
  interface ORMTableInput {
442
444
  table: string;
443
445
  columns: {
@@ -596,7 +598,7 @@ interface BaseTableInstance<ColumnTypes> {
596
598
  shape: Shape;
597
599
  };
598
600
  }, scopes: DbTableOptionScopes<Table, Shape, Keys>): CoreQueryScopes<Keys>;
599
- belongsTo<Columns extends ColumnsShapeBase, Related extends TableClass, Scope extends Query, Options extends BelongsToOptions<Columns, Related, Scope>>(this: {
601
+ belongsTo<Columns extends ColumnsShapeBase, Related extends TableClass, Options extends BelongsToOptions<Columns, Related>>(this: {
600
602
  columns: {
601
603
  shape: Columns;
602
604
  };
@@ -605,7 +607,7 @@ interface BaseTableInstance<ColumnTypes> {
605
607
  fn: () => Related;
606
608
  options: Options;
607
609
  };
608
- hasOne<Columns extends ColumnsShapeBase, Related extends TableClass, Scope extends Query, Through extends string, Source extends string, Options extends HasOneOptions<Columns, Related, Scope, Through, Source>>(this: {
610
+ hasOne<Columns extends ColumnsShapeBase, Related extends TableClass, Through extends string, Source extends string, Options extends HasOneOptions<Columns, Related, Through, Source>>(this: {
609
611
  columns: {
610
612
  shape: Columns;
611
613
  };
@@ -614,7 +616,7 @@ interface BaseTableInstance<ColumnTypes> {
614
616
  fn: () => Related;
615
617
  options: Options;
616
618
  };
617
- hasMany<Columns extends ColumnsShapeBase, Related extends TableClass, Scope extends Query, Through extends string, Source extends string, Options extends HasOneOptions<Columns, Related, Scope, Through, Source>>(this: {
619
+ hasMany<Columns extends ColumnsShapeBase, Related extends TableClass, Through extends string, Source extends string, Options extends HasOneOptions<Columns, Related, Through, Source>>(this: {
618
620
  columns: {
619
621
  shape: Columns;
620
622
  };
@@ -623,7 +625,7 @@ interface BaseTableInstance<ColumnTypes> {
623
625
  fn: () => Related;
624
626
  options: Options;
625
627
  };
626
- hasAndBelongsToMany<Columns extends ColumnsShapeBase, Related extends TableClass, Scope extends Query, Options extends HasAndBelongsToManyOptions<Columns, Related, Scope>>(this: {
628
+ hasAndBelongsToMany<Columns extends ColumnsShapeBase, Related extends TableClass, Options extends HasAndBelongsToManyOptions<Columns, Related>>(this: {
627
629
  columns: {
628
630
  shape: Columns;
629
631
  };
@@ -715,4 +717,4 @@ declare const createRepo: <T extends Query, Methods extends MethodsBase<T>>(tabl
715
717
  shape: T['shape'];
716
718
  }>(q: Q) => Query & Q & MapMethods<T, Methods>) & T, Methods>;
717
719
 
718
- export { type BaseTableClass, type BaseTableInstance, type Insertable, type MapMethods, type MapQueryMethods, type MethodsBase, type ORMTableInput, type ORMTableInputToQueryBuilder, type OrchidORM, type Queryable, type Repo, type ScopeFn, type Selectable, type SetColumnsResult, type Table, type TableClass, type TableClasses, type TableInfo, type TableToDb, type Updatable, createBaseTable, createRepo, orchidORM };
720
+ export { type BaseTableClass, type BaseTableInstance, type Insertable, type MapMethods, type MapQueryMethods, type MethodsBase, type ORMTableInput, type ORMTableInputToQueryBuilder, type OrchidORM, type Queryable, type Repo, type Selectable, type SetColumnsResult, type Table, type TableClass, type TableClasses, type TableInfo, type TableToDb, type Updatable, createBaseTable, createRepo, orchidORM };
package/dist/index.js CHANGED
@@ -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
- pqb.setQueryObjectValue(
229
- q,
230
- "joinedShapes",
231
- baseQuery.q.as || baseQuery.table,
232
- baseQuery.q.shape
233
- );
234
220
  const baseAs = pqb.getQueryAs(baseQuery);
221
+ const q = joiningQuery.clone();
222
+ pqb.setQueryObjectValueImmutable(q, "joinedShapes", baseAs, baseQuery.q.shape);
235
223
  for (let i = 0; i < len; i++) {
236
224
  pqb.pushQueryOnForOuter(
237
225
  q,
@@ -340,7 +328,7 @@ class BelongsToVirtualColumn extends pqb.VirtualColumn {
340
328
  const relationData = [values];
341
329
  store.belongsTo[key] = relationData;
342
330
  q.q.wrapInTransaction = true;
343
- pqb.pushQueryValue(q, "beforeCreate", async (q2) => {
331
+ pqb.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 pqb.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
+ pqb._queryWhere(query, [on]);
358
+ pqb._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
- pqb.setQueryObjectValue(
381
- q,
382
- "joinedShapes",
383
- baseQuery.q.as || baseQuery.table,
384
- baseQuery.q.shape
385
- );
386
371
  const baseAs = pqb.getQueryAs(baseQuery);
372
+ const q = joiningQuery.clone();
373
+ pqb.setQueryObjectValueImmutable(q, "joinedShapes", baseAs, baseQuery.q.shape);
387
374
  for (let i = 0; i < len; i++) {
388
375
  pqb.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
  pqb.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 pqb._queryUpdate(
556
- query.findBy(obj),
557
- upsert.update
558
- );
559
- } else {
555
+ const count = obj ? await pqb._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 pqb._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
+ pqb._queryWhere(query, [on]);
691
+ pqb._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 pqb._queryDefaults(query.where(values), values);
724
+ return pqb._queryDefaults(query.where(values), { ...on, ...values });
717
725
  },
718
726
  virtualColumn: new HasOneVirtualColumn(
719
727
  pqb.defaultSchemaConfig,
@@ -837,7 +845,9 @@ const nestedUpdate$2 = ({ query, primaryKeys, foreignKeys }) => {
837
845
  } else if (params.update) {
838
846
  await pqb._queryUpdate(currentRelationsQuery, params.update);
839
847
  } else if (params.delete) {
840
- await pqb._queryDelete(currentRelationsQuery);
848
+ const q = pqb._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
+ pqb._queryWhere(query, [on]);
962
+ pqb._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 pqb._queryDefaults(query.where(values), values);
995
+ return pqb._queryDefaults(query.where(values), { ...on, ...values });
981
996
  },
982
997
  virtualColumn: new HasManyVirtualColumn(
983
998
  pqb.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] = pqb._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 pqb.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 = orchidCore.toArray(params.add);
1161
+ const count = await pqb._queryUpdate(
1162
+ t.where({ OR: relatedWheres }),
1163
+ obj
1164
+ );
1165
+ if (count < relatedWheres.length) {
1166
+ throw new pqb.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
+ pqb._queryWhere(query, [on]);
1267
+ pqb._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] = orchidCore.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
- pqb._queryJoinOn(q, [
1379
+ pqb._queryJoinOn(q2, [
1334
1380
  throughForeignKeysFull[i],
1335
1381
  throughPrimaryKeysFull[i]
1336
1382
  ]);
1337
1383
  }
1338
- return pqb._queryWhere(q, [where]);
1384
+ return pqb._queryWhere(q2, [where]);
1339
1385
  });
1386
+ return on ? pqb._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
+ pqb._queryWhereExists(t, state.relatedTableQuery, [
1463
+ (q) => {
1464
+ for (let i = 0; i < state.throughPrimaryKeys.length; i++) {
1465
+ pqb._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 pqb._queryCreateMany(
1586
1646
  pqb._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 = orchidCore.toArray(params.add);
1694
+ const joinTableColumns = [
1695
+ ...state.foreignKeys,
1696
+ ...state.throughForeignKeys
1697
+ ];
1698
+ try {
1699
+ const count = await state.joinTableQuery.insertManyFrom(
1700
+ pqb._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) => orchidCore.pick(x, state.primaryKeys))
1723
+ )
1724
+ ).onConflict(joinTableColumns).merge([state.foreignKeys[0]]);
1725
+ if (count < data.length * relatedWheres.length) {
1726
+ throw new pqb.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 pqb.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 pqb._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(