orchid-orm 1.76.5 → 1.77.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,4 +1,4 @@
1
- import { DynamicRawSQL, QueryHooks, RawSql, VirtualColumn, _appendQuery, _appendQueryOnUpsertCreate, _clone, _createDbSqlMethod, _hookSelectColumns, _initQueryBuilder, _orCreate, _prependWith, _queryCreate, _queryCreateMany, _queryCreateManyFrom, _queryDefaults, _queryDelete, _queryFindBy, _queryFindByOptional, _queryHookAfterCreate, _queryHookAfterUpdate, _queryInsert, _queryInsertMany, _queryJoinOn, _queryRows, _querySelect, _queryTake, _queryTakeOptional, _queryUpdate, _queryUpdateOrThrow, _queryUpsert, _queryWhere, _queryWhereExists, _queryWhereIn, applyMixins, cloneQueryBaseUnscoped, defaultSchemaConfig, emptyArray, emptyObject, getCallerFilePath, getClonedQueryData, getColumnTypes, getFreeAlias, getPrimaryKeys, getQueryAs, getShapeFromSelect, getStackTrace, internalSchemaConfig, isExpression, isQueryReturnsAll, makeColumnTypes, noop, objectHasValues, parseTableData, pick, prepareSubQueryForSql, pushQueryOnForOuter, setQueryObjectValueImmutable, toArray, toSnakeCase } from "pqb/internal";
1
+ import { DynamicRawSQL, QueryHooks, RawSql, VirtualColumn, _appendQuery, _appendQueryOnUpsertCreate, _clone, _createDbSqlMethod, _hookSelectColumns, _initQueryBuilder, _onUpsertUpdate, _orCreate, _prependWith, _prependWithOnUpsertCreate, _queryCreate, _queryCreateMany, _queryCreateManyFrom, _queryDefaults, _queryDelete, _queryFindBy, _queryFindByOptional, _queryHookAfterCreate, _queryHookAfterUpdate, _queryInsert, _queryInsertMany, _queryInsertManyFrom, _queryJoinOn, _queryRows, _querySelect, _queryTake, _queryTakeOptional, _queryUpdate, _queryUpdateOrThrow, _queryUpsert, _queryWhere, _queryWhereExists, _queryWhereIn, applyMixins, cloneQueryBaseUnscoped, defaultSchemaConfig, emptyArray, emptyObject, getCallerFilePath, getClonedQueryData, getColumnTypes, getForeignKeyTableInstance, getFreeAlias, getPrimaryKeys, getQueryAs, getShapeFromSelect, getStackTrace, internalSchemaConfig, isExpression, isQueryReturnsAll, makeColumnTypes, noop, objectHasValues, parseTableData, parseTableDataInput, pick, prepareSubQueryForSql, pushQueryOnForOuter, setQueryObjectValueImmutable, tableDataMethods, toArray, toSnakeCase } from "pqb/internal";
2
2
  import { Db, NotFoundError, OrchidOrmInternalError } from "pqb";
3
3
  import { AsyncLocalStorage } from "node:async_hooks";
4
4
  export * from "pqb";
@@ -152,6 +152,422 @@ function createBaseTable({ schemaConfig = defaultSchemaConfig, columnTypes: colu
152
152
  base.prototype.autoForeignKeys = autoForeignKeys === true ? {} : autoForeignKeys || void 0;
153
153
  return base;
154
154
  }
155
+ const makeRelationEndpoint = (id, columns, on, through, source) => {
156
+ const endpoint = {
157
+ id,
158
+ columns,
159
+ on,
160
+ relationThrough: through,
161
+ relationSource: source,
162
+ where(conditions) {
163
+ return makeRelationEndpoint(id, columns, {
164
+ ...on,
165
+ ...conditions
166
+ }, through, source);
167
+ }
168
+ };
169
+ if (through) {
170
+ endpoint.through = through;
171
+ endpoint.source = source;
172
+ } else endpoint.through = (through, source) => makeRelationEndpoint(id, columns, on, through, source);
173
+ return endpoint;
174
+ };
175
+ const getRelationTargetId = (target) => {
176
+ const item = target;
177
+ const id = item.id ?? item.data?.id;
178
+ if (!id) throw new Error("Cannot get relation target id");
179
+ return id;
180
+ };
181
+ const getRelationEndpointOptions = (endpoint) => endpoint.on ? {
182
+ references: endpoint.columns,
183
+ on: endpoint.on
184
+ } : { references: endpoint.columns };
185
+ const getRelationThroughOptions = (endpoint) => {
186
+ const options = {
187
+ through: endpoint.relationThrough,
188
+ source: endpoint.relationSource
189
+ };
190
+ if (endpoint.on) options.on = endpoint.on;
191
+ return options;
192
+ };
193
+ const makeRelationBuilder = () => {
194
+ const withRequired = (relation) => {
195
+ const item = relation;
196
+ item.required = (value = true) => {
197
+ item.options.required = value;
198
+ return item;
199
+ };
200
+ item.foreignKey = (value = true) => {
201
+ item.options.foreignKey = value;
202
+ return item;
203
+ };
204
+ return item;
205
+ };
206
+ const builder = ((...columns) => ({
207
+ belongsTo: (fn) => {
208
+ const endpoint = fn();
209
+ return withRequired({
210
+ type: "belongsTo",
211
+ id: getRelationTargetId(endpoint),
212
+ options: {
213
+ columns,
214
+ ...getRelationEndpointOptions(endpoint)
215
+ }
216
+ });
217
+ },
218
+ hasOne: (fn) => {
219
+ const endpoint = fn();
220
+ return withRequired({
221
+ type: "hasOne",
222
+ id: getRelationTargetId(endpoint),
223
+ options: {
224
+ columns,
225
+ ...getRelationEndpointOptions(endpoint)
226
+ }
227
+ });
228
+ },
229
+ hasMany: (fn) => {
230
+ const endpoint = fn();
231
+ return withRequired({
232
+ type: "hasMany",
233
+ id: getRelationTargetId(endpoint),
234
+ options: {
235
+ columns,
236
+ ...getRelationEndpointOptions(endpoint)
237
+ }
238
+ });
239
+ },
240
+ hasAndBelongsToMany: (fn) => {
241
+ const endpoint = fn();
242
+ const options = { columns };
243
+ return {
244
+ type: "hasAndBelongsToMany",
245
+ id: getRelationTargetId(endpoint),
246
+ options,
247
+ through(joinTable, selfColumns, relColumns, throughOptions) {
248
+ this.options.references = Array.isArray(selfColumns) ? selfColumns : [selfColumns];
249
+ this.options.on = endpoint.on;
250
+ const schemaIndex = joinTable.indexOf(".");
251
+ const parsedSchema = schemaIndex === -1 ? void 0 : joinTable.slice(0, schemaIndex);
252
+ const table = schemaIndex === -1 ? joinTable : joinTable.slice(schemaIndex + 1);
253
+ this.options.through = {
254
+ schema: throughOptions?.schema ?? parsedSchema,
255
+ table,
256
+ snakeCase: throughOptions?.joinTableSnakeCase,
257
+ columns: Array.isArray(relColumns) ? relColumns : [relColumns],
258
+ references: endpoint.columns
259
+ };
260
+ return this;
261
+ },
262
+ foreignKey(value) {
263
+ if (value === false) {
264
+ this.options.foreignKey = false;
265
+ if (this.options.through) this.options.through.foreignKey = false;
266
+ } else if (value === void 0 || value === true) {
267
+ this.options.foreignKey = true;
268
+ if (this.options.through) this.options.through.foreignKey = true;
269
+ } else if ("forBothTables" in value) {
270
+ this.options.foreignKey = value.forBothTables ?? true;
271
+ if (this.options.through) this.options.through.foreignKey = value.forBothTables ?? true;
272
+ } else {
273
+ this.options.foreignKey = value.forThisTable ?? true;
274
+ if (this.options.through) this.options.through.foreignKey = value.forRelatedTable ?? true;
275
+ }
276
+ return this;
277
+ }
278
+ };
279
+ }
280
+ }));
281
+ builder.hasOne = (fn) => {
282
+ const endpoint = fn();
283
+ return withRequired({
284
+ type: "hasOne",
285
+ id: getRelationTargetId(endpoint),
286
+ options: getRelationThroughOptions(endpoint)
287
+ });
288
+ };
289
+ builder.hasMany = (fn) => {
290
+ const endpoint = fn();
291
+ return {
292
+ type: "hasMany",
293
+ id: getRelationTargetId(endpoint),
294
+ options: getRelationThroughOptions(endpoint)
295
+ };
296
+ };
297
+ return builder;
298
+ };
299
+ var InitHookTarget = class extends QueryHooks {
300
+ constructor(q) {
301
+ super();
302
+ this.q = q;
303
+ }
304
+ clone() {
305
+ return this;
306
+ }
307
+ };
308
+ const setDefinitionSchemaMethods = (definition, schema, shape) => {
309
+ definition.prototype ??= {};
310
+ definition.prototype.columns = { shape };
311
+ definition.inputSchema = schema.inputSchema;
312
+ definition.outputSchema = schema.outputSchema;
313
+ definition.querySchema = schema.querySchema;
314
+ definition.pkeySchema = schema.pkeySchema;
315
+ definition.createSchema = schema.createSchema;
316
+ definition.updateSchema = schema.updateSchema;
317
+ };
318
+ const createTableFactory = (options) => {
319
+ const schema = (options?.schemaConfig ?? defaultSchemaConfig)();
320
+ const defaultColumnTypes = makeColumnTypes(schema);
321
+ const columnTypesOption = options?.columnTypes;
322
+ const columnTypes = typeof columnTypesOption === "function" ? columnTypesOption(defaultColumnTypes) : columnTypesOption || defaultColumnTypes;
323
+ const defineTableExportAs = options?.defineTableExportAs ?? "defineTable";
324
+ const filePathOrStack = options?.filePath || getStackTrace();
325
+ let filePath;
326
+ const defineTable = ((table, optionsOrT, maybeT) => {
327
+ const tableOptions = typeof optionsOrT === "function" ? void 0 : optionsOrT;
328
+ const t = typeof optionsOrT === "function" ? optionsOrT : maybeT;
329
+ if (!t) throw new Error("Column definitions are required");
330
+ const id = tableOptions?.id ?? table;
331
+ const shape = getColumnTypes(columnTypes, t, options?.nowSQL, options?.language);
332
+ const tableData = {};
333
+ const snakeCase = tableOptions?.snakeCase ?? options?.snakeCase;
334
+ if (snakeCase) for (const key in shape) {
335
+ const column = shape[key];
336
+ if (column.data.name) continue;
337
+ const snakeName = toSnakeCase(key);
338
+ if (snakeName !== key) column.data.name = snakeName;
339
+ }
340
+ const nameInDb = tableOptions?.nameInDb ?? (snakeCase ? toSnakeCase(table) : table);
341
+ const autoForeignKeys = tableOptions?.autoForeignKeys !== void 0 ? tableOptions.autoForeignKeys : options?.autoForeignKeys;
342
+ const instance = {
343
+ id,
344
+ table,
345
+ nameInDb,
346
+ schema: tableOptions?.schema,
347
+ noPrimaryKey: tableOptions?.noPrimaryKey,
348
+ snakeCase,
349
+ comment: tableOptions?.comment,
350
+ generatorIgnore: tableOptions?.generatorIgnore,
351
+ readOnly: tableOptions?.readOnly,
352
+ columns: {
353
+ shape,
354
+ data: tableData
355
+ },
356
+ types: columnTypes,
357
+ filePath: options?.filePath ?? "",
358
+ language: options?.language,
359
+ autoForeignKeys: autoForeignKeys === true ? {} : autoForeignKeys || void 0,
360
+ q: {}
361
+ };
362
+ const definition = ((...columns) => makeRelationEndpoint(id, columns));
363
+ definition.data = {
364
+ id,
365
+ table,
366
+ name: void 0,
367
+ relations: void 0,
368
+ columns: shape,
369
+ types: void 0,
370
+ computed: void 0,
371
+ scopes: void 0,
372
+ readOnly: tableOptions?.readOnly,
373
+ tableData
374
+ };
375
+ setDefinitionSchemaMethods(definition, schema, shape);
376
+ definition.exportAs = defineTableExportAs;
377
+ definition.nowSQL = options?.nowSQL;
378
+ definition.instance = () => {
379
+ const { computed, relations, scopes, softDelete, readOnly } = definition.data;
380
+ instance.computed = computed;
381
+ instance.scopes = scopes;
382
+ instance.softDelete = softDelete;
383
+ instance.readOnly = readOnly;
384
+ instance.relations = typeof relations === "function" ? relations(makeRelationBuilder()) : relations;
385
+ return instance;
386
+ };
387
+ definition.where = function(conditions) {
388
+ return makeRelationEndpoint(id, [], conditions);
389
+ };
390
+ definition.through = (through, source) => makeRelationEndpoint(id, [], void 0, through, source);
391
+ definition.primaryKey = (...args) => {
392
+ parseTableDataInput(tableData, tableDataMethods.primaryKey(...args));
393
+ return definition;
394
+ };
395
+ definition.index = (...args) => {
396
+ parseTableDataInput(tableData, tableDataMethods.index(...args));
397
+ return definition;
398
+ };
399
+ definition.searchIndex = (...args) => {
400
+ parseTableDataInput(tableData, tableDataMethods.searchIndex(...args));
401
+ return definition;
402
+ };
403
+ definition.unique = (...args) => {
404
+ parseTableDataInput(tableData, tableDataMethods.unique(...args));
405
+ return definition;
406
+ };
407
+ definition.exclude = (...args) => {
408
+ parseTableDataInput(tableData, tableDataMethods.exclude(...args));
409
+ return definition;
410
+ };
411
+ definition.check = (...args) => {
412
+ parseTableDataInput(tableData, tableDataMethods.check(...args));
413
+ return definition;
414
+ };
415
+ definition.softDelete = (column) => {
416
+ definition.data.softDelete = column || true;
417
+ return definition;
418
+ };
419
+ definition.computed = (computed) => {
420
+ definition.data.computed = computed;
421
+ return definition;
422
+ };
423
+ definition.scopes = (scopes) => {
424
+ definition.data.scopes = scopes;
425
+ return definition;
426
+ };
427
+ definition.grants = (grants) => {
428
+ instance.grants = grants;
429
+ return definition;
430
+ };
431
+ definition.rls = (rls) => {
432
+ instance.rls = rls;
433
+ return definition;
434
+ };
435
+ definition.init = (fn) => {
436
+ instance.init = function(orm) {
437
+ fn(orm, new InitHookTarget(this.q));
438
+ };
439
+ return definition;
440
+ };
441
+ definition.foreignKey = (columns, fnOrTable, foreignColumns, options) => {
442
+ parseTableDataInput(tableData, tableDataMethods.foreignKey(columns, fnOrTable, foreignColumns, options));
443
+ return definition;
444
+ };
445
+ definition.relations = (fn) => {
446
+ definition.data.relations = fn;
447
+ return definition;
448
+ };
449
+ return definition;
450
+ });
451
+ defineTable.getFilePath = () => {
452
+ if (filePath) return filePath;
453
+ if (typeof filePathOrStack === "string") return filePath = filePathOrStack;
454
+ filePath = getCallerFilePath(filePathOrStack);
455
+ if (filePath) return filePath;
456
+ throw new Error("Failed to determine file path of a table factory. Please set the `filePath` option of `createTableFactory` manually.");
457
+ };
458
+ defineTable.types = columnTypes;
459
+ defineTable.exportAs = defineTableExportAs;
460
+ defineTable.nowSQL = options?.nowSQL;
461
+ defineTable.snakeCase = options?.snakeCase;
462
+ defineTable.language = options?.language;
463
+ const defineView = ((name, optionsOrT, maybeT) => {
464
+ const viewOptions = typeof optionsOrT === "function" ? void 0 : optionsOrT;
465
+ const materialized = viewOptions?.materialized;
466
+ const readOnly = materialized || viewOptions?.readOnly !== false;
467
+ const t = typeof optionsOrT === "function" ? optionsOrT : maybeT;
468
+ if (!t) throw new Error("Column definitions are required");
469
+ const language = viewOptions?.language ?? options?.language;
470
+ const snakeCase = viewOptions?.snakeCase ?? options?.snakeCase;
471
+ const shape = getColumnTypes(columnTypes, t, options?.nowSQL, language);
472
+ const tableData = {};
473
+ const nameInDb = viewOptions?.nameInDb ?? (snakeCase ? toSnakeCase(name) : name);
474
+ if (snakeCase) for (const key in shape) {
475
+ const column = shape[key];
476
+ if (column.data.name) continue;
477
+ const snakeName = toSnakeCase(key);
478
+ if (snakeName !== key) column.data.name = snakeName;
479
+ }
480
+ const instance = {
481
+ id: viewOptions?.id ?? name,
482
+ name,
483
+ nameInDb,
484
+ schema: viewOptions?.schema,
485
+ snakeCase,
486
+ columns: {
487
+ shape,
488
+ data: tableData
489
+ },
490
+ types: columnTypes,
491
+ filePath: options?.filePath ?? "",
492
+ language,
493
+ readOnly,
494
+ materialized,
495
+ generatorIgnore: viewOptions?.generatorIgnore,
496
+ withData: viewOptions?.withData,
497
+ sql: viewOptions?.sql,
498
+ recursive: viewOptions?.recursive,
499
+ checkOption: viewOptions?.checkOption,
500
+ securityBarrier: viewOptions?.securityBarrier,
501
+ securityInvoker: viewOptions?.securityInvoker,
502
+ q: {}
503
+ };
504
+ const viewId = viewOptions?.id ?? name;
505
+ const definition = ((...columns) => makeRelationEndpoint(viewId, columns));
506
+ definition.data = {
507
+ id: viewOptions?.id ?? name,
508
+ table: void 0,
509
+ name,
510
+ relations: void 0,
511
+ columns: shape,
512
+ types: void 0,
513
+ computed: void 0,
514
+ scopes: void 0,
515
+ readOnly,
516
+ materialized,
517
+ tableData
518
+ };
519
+ setDefinitionSchemaMethods(definition, schema, shape);
520
+ definition.nowSQL = options?.nowSQL;
521
+ definition.instance = () => {
522
+ const { computed, relations, scopes, softDelete } = definition.data;
523
+ instance.computed = computed;
524
+ instance.scopes = scopes;
525
+ instance.softDelete = softDelete;
526
+ instance.relations = typeof relations === "function" ? relations(makeRelationBuilder()) : relations;
527
+ return instance;
528
+ };
529
+ definition.query = (fn) => {
530
+ instance.init = (orm) => {
531
+ instance.query = fn(orm);
532
+ };
533
+ return definition;
534
+ };
535
+ definition.through = (through, source) => makeRelationEndpoint(viewId, [], void 0, through, source);
536
+ definition.softDelete = (column) => {
537
+ definition.data.softDelete = column || true;
538
+ return definition;
539
+ };
540
+ definition.computed = (computed) => {
541
+ definition.data.computed = computed;
542
+ return definition;
543
+ };
544
+ definition.scopes = (scopes) => {
545
+ definition.data.scopes = scopes;
546
+ return definition;
547
+ };
548
+ definition.grants = (grants) => {
549
+ instance.grants = grants;
550
+ return definition;
551
+ };
552
+ definition.init = (fn) => {
553
+ instance.init = function(orm) {
554
+ fn(orm, new InitHookTarget(this.q));
555
+ };
556
+ return definition;
557
+ };
558
+ definition.relations = (fn) => {
559
+ definition.data.relations = fn;
560
+ return definition;
561
+ };
562
+ return definition;
563
+ });
564
+ return {
565
+ defineTable,
566
+ defineView,
567
+ sql: _createDbSqlMethod(columnTypes),
568
+ exportAs: defineTableExportAs
569
+ };
570
+ };
155
571
  const throwIfQueryReturnsAllForNestedUpdate = (q, params) => {
156
572
  if (!(params.set || params.create || params.upsert) || !isQueryReturnsAll(q)) return;
157
573
  let key;
@@ -231,7 +647,7 @@ const addAutoForeignKey = (tableConfig, from, to, primaryKeys, foreignKeys, opti
231
647
  fkeyTable = fkey.fnOrTable;
232
648
  fkeyColumn = getColumnKeyFromDbName(to, fkeyColumn);
233
649
  } else {
234
- const instance = fkey.fnOrTable().instance();
650
+ const instance = getForeignKeyTableInstance(fkey.fnOrTable());
235
651
  fkeyTable = instance.nameInDb || instance.table;
236
652
  }
237
653
  if (toTable === fkeyTable && pkey === fkeyColumn) return;
@@ -247,7 +663,7 @@ const addAutoForeignKey = (tableConfig, from, to, primaryKeys, foreignKeys, opti
247
663
  let sameForeignTable = false;
248
664
  if (typeof refs.fnOrTable === "string") sameForeignTable = refs.fnOrTable === toTable && refs.foreignColumns.every((column, i) => getColumnKeyFromDbName(to, column) === sortedPkeys[i]);
249
665
  else {
250
- const instance = refs.fnOrTable().instance();
666
+ const instance = getForeignKeyTableInstance(refs.fnOrTable());
251
667
  sameForeignTable = (instance.nameInDb || instance.table) === toTable && refs.foreignColumns.every((column, i) => column === sortedPkeys[i]);
252
668
  }
253
669
  if (refs.columns.length === sortedFkeys.length && refs.columns.every((column, i) => column === sortedFkeys[i]) && refs.foreignColumns.length === sortedPkeys.length && sameForeignTable) return;
@@ -394,24 +810,39 @@ var BelongsToVirtualColumn = class extends VirtualColumn {
394
810
  if (!nestedCreateItems) return;
395
811
  for (const key of foreignKeys) if (!ctx.columns.has(key)) ctx.columns.set(key, ctx.columns.size);
396
812
  const { create, connect, connectOrCreate } = nestedCreateItems;
397
- if (create) _prependWith(queryForCreate, (as) => {
398
- const count = create.items.length;
399
- foreignKeys.forEach((foreignKey, i) => {
400
- const primaryKey = primaryKeys[i];
401
- create.items.forEach((item, i) => {
402
- item[foreignKey]._sql = selectCteColumnFromManySql(as, primaryKey, i, count);
813
+ if (create) {
814
+ const selectPKeys = query.select(...primaryKeys);
815
+ let createQuery;
816
+ if (queryForCreate.q.type === "upsert") {
817
+ const createWhereNotExists = new RawSql("");
818
+ _onUpsertUpdate(queryForCreate, (as) => {
819
+ createWhereNotExists._sql = `NOT EXISTS (SELECT 1 FROM "${as}")`;
403
820
  });
404
- });
405
- }, _queryInsertMany(query.select(...primaryKeys), create.values));
821
+ createQuery = _queryInsertManyFrom(selectPKeys, _queryWhere(_queryTake(_querySelect(_clone(query.qb), [{}])), [createWhereNotExists]), create.values);
822
+ } else createQuery = _queryInsertMany(selectPKeys, create.values);
823
+ const setForeignKeys = (as) => {
824
+ const count = create.items.length;
825
+ foreignKeys.forEach((foreignKey, i) => {
826
+ const primaryKey = primaryKeys[i];
827
+ create.items.forEach((item, i) => {
828
+ item[foreignKey]._sql = selectCteColumnFromManySql(as, primaryKey, i, count);
829
+ });
830
+ });
831
+ };
832
+ if (queryForCreate.q.type === "upsert") _prependWithOnUpsertCreate(queryForCreate, setForeignKeys, createQuery);
833
+ else _prependWith(queryForCreate, setForeignKeys, createQuery);
834
+ }
406
835
  if (connect) connect.values.forEach((value, itemI) => {
407
836
  const as = getFreeAlias(queryForCreate.q.withShapes, "q");
408
- _prependWith(queryForCreate, as, query.select(...primaryKeys).findBy(value));
837
+ (queryForCreate.q.type === "upsert" ? _prependWithOnUpsertCreate : _prependWith)(queryForCreate, as, query.select(...primaryKeys).findBy(value));
409
838
  foreignKeys.map((foreignKey, i) => {
410
839
  connect.items[itemI][foreignKey] = new RawSql(selectCteColumnMustExistSql(i, as, primaryKeys[i]));
411
840
  });
412
841
  });
413
842
  if (connectOrCreate) connectOrCreate.values.forEach((value, itemI) => {
414
- _prependWith(queryForCreate, setForeignKeysFromCte(connectOrCreate.items[itemI], primaryKeys, foreignKeys), _orCreate(_queryWhere(query.select(...primaryKeys), [value.where]), value.create));
843
+ const asFn = setForeignKeysFromCte(connectOrCreate.items[itemI], primaryKeys, foreignKeys);
844
+ const selectPKeys = query.select(...primaryKeys);
845
+ (queryForCreate.q.type === "upsert" ? _prependWithOnUpsertCreate : _prependWith)(queryForCreate, asFn, _orCreate(_queryWhere(selectPKeys, [value.where]), value.create));
415
846
  });
416
847
  }
417
848
  update(q, set) {
@@ -580,31 +1011,100 @@ var HasOneVirtualColumn = class extends VirtualColumn {
580
1011
  });
581
1012
  if (!nestedCreateItems) return;
582
1013
  let createAs;
1014
+ let createWhereExists;
1015
+ let createSelectAs;
1016
+ let createAliasedPrimaryKeys;
1017
+ let upsertCreateAs;
583
1018
  let connectAs;
584
1019
  let connectOrCreateAs;
1020
+ const setUpsertCreatePlaceholders = () => {
1021
+ if (!upsertCreateAs || !createAliasedPrimaryKeys) return;
1022
+ const aliasedPrimaryKeys = createAliasedPrimaryKeys;
1023
+ if (createWhereExists) createWhereExists._sql = `EXISTS (SELECT 1 FROM "${upsertCreateAs}")`;
1024
+ if (createSelectAs) foreignKeys.forEach((key, keyI) => {
1025
+ createSelectAs[key]._sql = selectCteColumnFromManySql(upsertCreateAs, aliasedPrimaryKeys[keyI], 0, 1);
1026
+ });
1027
+ if (connect) foreignKeys.forEach((key, keyI) => {
1028
+ const primaryKey = aliasedPrimaryKeys[keyI];
1029
+ for (let i = 0; i < connect.items.length; i++) connect.items[i][key]._sql = selectCteColumnFromManySql(upsertCreateAs, primaryKey, connect.indexes[i], count);
1030
+ });
1031
+ if (connectOrCreate) foreignKeys.forEach((key, keyI) => {
1032
+ const primaryKey = aliasedPrimaryKeys[keyI];
1033
+ for (let i = 0; i < connectOrCreate.items.length; i++) connectOrCreate.items[i][key]._sql = selectCteColumnFromManySql(upsertCreateAs, primaryKey, connectOrCreate.indexes[i], count);
1034
+ });
1035
+ };
585
1036
  _hookSelectColumns(querySelf, primaryKeys, (aliasedPrimaryKeys) => {
1037
+ createAliasedPrimaryKeys = aliasedPrimaryKeys;
586
1038
  foreignKeys.forEach((key, keyI) => {
587
1039
  const primaryKey = aliasedPrimaryKeys[keyI];
588
1040
  if (create && createAs) for (let i = 0; i < create.items.length; i++) create.items[i][key]._sql = selectCteColumnFromManySql(createAs, primaryKey, create.indexes[i], count);
589
1041
  if (connect && connectAs) for (let i = 0; i < connect.items.length; i++) connect.items[i][key]._sql = selectCteColumnFromManySql(connectAs, primaryKey, connect.indexes[i], count);
590
1042
  if (connectOrCreate && connectOrCreateAs) for (let i = 0; i < connectOrCreate.items.length; i++) connectOrCreate.items[i][key]._sql = selectCteColumnFromManySql(connectOrCreateAs, primaryKey, connectOrCreate.indexes[i], count);
591
1043
  });
1044
+ setUpsertCreatePlaceholders();
592
1045
  });
593
1046
  const { create, connect, connectOrCreate } = nestedCreateItems;
594
- if (create) _appendQuery(querySelf, _queryInsertMany(_clone(rel), create.items), (as) => createAs = as);
1047
+ if (create) {
1048
+ let query;
1049
+ if (querySelf.q.type === "upsert") {
1050
+ createWhereExists = new RawSql("");
1051
+ createSelectAs = {};
1052
+ for (const key of foreignKeys) createSelectAs[key] = new RawSql("");
1053
+ const sourceQuery = _queryWhere(_queryTake(_querySelect(_clone(rel.qb), [createSelectAs])), [createWhereExists]);
1054
+ query = _queryInsertManyFrom(_clone(rel), sourceQuery, create.items);
1055
+ } else query = _queryInsertMany(_clone(rel), create.items);
1056
+ if (querySelf.q.type === "upsert") _appendQueryOnUpsertCreate(querySelf, query, (as) => {
1057
+ upsertCreateAs = as;
1058
+ setUpsertCreatePlaceholders();
1059
+ });
1060
+ else _appendQuery(querySelf, query, (as) => createAs = as);
1061
+ }
595
1062
  if (connect) connect.values.forEach((value, i) => {
1063
+ const connectWhereExists = querySelf.q.type === "upsert" ? new RawSql("") : void 0;
596
1064
  const query = _queryUpdateOrThrow(rel.where(value), connect.items[i]);
1065
+ if (connectWhereExists) _queryWhere(query, [connectWhereExists]);
597
1066
  query.q.ensureCount = 1;
598
- _appendQuery(querySelf, query, (as) => connectAs = as);
1067
+ (querySelf.q.type === "upsert" ? _appendQueryOnUpsertCreate : _appendQuery)(querySelf, query, (as) => {
1068
+ upsertCreateAs = as;
1069
+ setUpsertCreatePlaceholders();
1070
+ connectAs = as;
1071
+ if (connectWhereExists) connectWhereExists._sql = `EXISTS (SELECT 1 FROM "${as}")`;
1072
+ });
599
1073
  });
600
1074
  if (connectOrCreate) connectOrCreate.values.forEach((value, i) => {
601
- _appendQuery(querySelf, _queryUpsert(rel.where(value.where), {
602
- update: connectOrCreate.items[i],
1075
+ const foreignKeyValues = connectOrCreate.items[i];
1076
+ const connectOrCreateWhereExists = querySelf.q.type === "upsert" ? new RawSql("") : void 0;
1077
+ const connectOrCreateSelectAs = {};
1078
+ if (connectOrCreateWhereExists) for (const key of foreignKeys) {
1079
+ const value = foreignKeyValues[key];
1080
+ value.result = { value: rel.qb.shape[key] };
1081
+ connectOrCreateSelectAs[key] = value;
1082
+ }
1083
+ const query = _queryUpsert(rel.where(value.where), {
1084
+ update: foreignKeyValues,
603
1085
  create: {
604
1086
  ...value.create,
605
- ...connectOrCreate.items[i]
1087
+ ...foreignKeyValues
606
1088
  }
607
- }), (as) => connectOrCreateAs = as);
1089
+ });
1090
+ if (connectOrCreateWhereExists) {
1091
+ _queryWhere(query, [connectOrCreateWhereExists]);
1092
+ const guardQuery = _queryWhere(_queryTake(_querySelect(_clone(rel.qb), [{ one: new RawSql("1") }])), [connectOrCreateWhereExists]);
1093
+ const sourceQuery = _querySelect(_clone(rel.qb).from(guardQuery), [connectOrCreateSelectAs]);
1094
+ const q = query.q;
1095
+ const columns = q.columns || [];
1096
+ const foreignKeysSet = new Set(foreignKeys);
1097
+ q.columns = [...foreignKeys, ...columns.filter((column) => !foreignKeysSet.has(column))];
1098
+ q.values = q.values.map((row) => row.filter((_, i) => !foreignKeysSet.has(columns[i])));
1099
+ q.queryColumnsCount = foreignKeys.length;
1100
+ q.insertFrom = prepareSubQueryForSql(query, sourceQuery);
1101
+ }
1102
+ (querySelf.q.type === "upsert" ? _appendQueryOnUpsertCreate : _appendQuery)(querySelf, query, (as) => {
1103
+ upsertCreateAs = as;
1104
+ setUpsertCreatePlaceholders();
1105
+ connectOrCreateAs = as;
1106
+ if (connectOrCreateWhereExists) connectOrCreateWhereExists._sql = `EXISTS (SELECT 1 FROM "${as}")`;
1107
+ });
608
1108
  });
609
1109
  } else hasRelationHandleCreate(querySelf, ctx, items, rowIndexes, this.key, this.state.primaryKeys, this.nestedInsert);
610
1110
  }
@@ -633,7 +1133,8 @@ var HasOneVirtualColumn = class extends VirtualColumn {
633
1133
  const makeHasOneMethod = (tableConfig, table, relation, relationName, query) => {
634
1134
  const relPKeys = getPrimaryKeys(query);
635
1135
  if ("through" in relation.options) {
636
- const { through, source } = relation.options;
1136
+ const { through, source, on } = relation.options;
1137
+ if (on) _queryWhere(query, [on]);
637
1138
  const throughRelation = getThroughRelation(table, through);
638
1139
  const sourceRelation = getSourceRelation(throughRelation, source);
639
1140
  const sourceRelationQuery = sourceRelation.query.as(relationName);
@@ -777,9 +1278,30 @@ var HasManyVirtualColumn = class extends VirtualColumn {
777
1278
  });
778
1279
  if (!nestedCreateItems) return;
779
1280
  let createAs;
1281
+ let createWhereExists;
1282
+ let createSelectAs;
1283
+ let createAliasedPrimaryKeys;
1284
+ let upsertCreateAs;
780
1285
  let connectAs;
781
1286
  let connectOrCreateAs;
1287
+ const setUpsertCreatePlaceholders = () => {
1288
+ if (!upsertCreateAs || !createAliasedPrimaryKeys) return;
1289
+ const aliasedPrimaryKeys = createAliasedPrimaryKeys;
1290
+ if (createWhereExists) createWhereExists._sql = `EXISTS (SELECT 1 FROM "${upsertCreateAs}")`;
1291
+ if (createSelectAs) foreignKeys.forEach((key, keyI) => {
1292
+ createSelectAs[key]._sql = selectCteColumnFromManySql(upsertCreateAs, aliasedPrimaryKeys[keyI], 0, 1);
1293
+ });
1294
+ if (connect) foreignKeys.forEach((key, keyI) => {
1295
+ const primaryKey = aliasedPrimaryKeys[keyI];
1296
+ for (let i = 0; i < connect.items.length; i++) connect.items[i][key]._sql = selectCteColumnFromManySql(upsertCreateAs, primaryKey, connect.indexes[i], count);
1297
+ });
1298
+ if (connectOrCreate) foreignKeys.forEach((key, keyI) => {
1299
+ const primaryKey = aliasedPrimaryKeys[keyI];
1300
+ for (let i = 0; i < connectOrCreate.items.length; i++) connectOrCreate.items[i][key]._sql = selectCteColumnFromManySql(upsertCreateAs, primaryKey, connectOrCreate.indexes[i], count);
1301
+ });
1302
+ };
782
1303
  _hookSelectColumns(querySelf, primaryKeys, (aliasedPrimaryKeys) => {
1304
+ createAliasedPrimaryKeys = aliasedPrimaryKeys;
783
1305
  foreignKeys.forEach((key, keyI) => {
784
1306
  const primaryKey = aliasedPrimaryKeys[keyI];
785
1307
  if (create && createAs) for (let i = 0; i < create.items.length; i++) {
@@ -789,23 +1311,72 @@ var HasManyVirtualColumn = class extends VirtualColumn {
789
1311
  if (connect && connectAs) for (let i = 0; i < connect.items.length; i++) connect.items[i][key]._sql = selectCteColumnFromManySql(connectAs, primaryKey, connect.indexes[i], count);
790
1312
  if (connectOrCreate && connectOrCreateAs) for (let i = 0; i < connectOrCreate.items.length; i++) connectOrCreate.items[i][key]._sql = selectCteColumnFromManySql(connectOrCreateAs, primaryKey, connectOrCreate.indexes[i], count);
791
1313
  });
1314
+ setUpsertCreatePlaceholders();
792
1315
  });
793
1316
  const { create, connect, connectOrCreate } = nestedCreateItems;
794
- if (create) _appendQuery(querySelf, _queryInsertMany(_clone(rel), create.items.flat()), (as) => createAs = as);
1317
+ if (create) {
1318
+ let query;
1319
+ if (querySelf.q.type === "upsert") {
1320
+ createWhereExists = new RawSql("");
1321
+ createSelectAs = {};
1322
+ for (const key of foreignKeys) createSelectAs[key] = new RawSql("");
1323
+ const sourceQuery = _queryWhere(_queryTake(_querySelect(_clone(rel.qb), [createSelectAs])), [createWhereExists]);
1324
+ query = _queryInsertManyFrom(_clone(rel), sourceQuery, create.items.flat());
1325
+ } else query = _queryInsertMany(_clone(rel), create.items.flat());
1326
+ if (querySelf.q.type === "upsert") _appendQueryOnUpsertCreate(querySelf, query, (as) => {
1327
+ upsertCreateAs = as;
1328
+ setUpsertCreatePlaceholders();
1329
+ });
1330
+ else _appendQuery(querySelf, query, (as) => createAs = as);
1331
+ }
795
1332
  if (connect) connect.values.forEach((value, i) => {
1333
+ const connectWhereExists = querySelf.q.type === "upsert" ? new RawSql("") : void 0;
796
1334
  const query = _queryUpdateOrThrow(rel.whereOneOf(...value), connect.items[i]);
1335
+ if (connectWhereExists) _queryWhere(query, [connectWhereExists]);
797
1336
  query.q.ensureCount = value.length;
798
- _appendQuery(querySelf, query, (as) => connectAs = as);
1337
+ (querySelf.q.type === "upsert" ? _appendQueryOnUpsertCreate : _appendQuery)(querySelf, query, (as) => {
1338
+ upsertCreateAs = as;
1339
+ setUpsertCreatePlaceholders();
1340
+ connectAs = as;
1341
+ if (connectWhereExists) connectWhereExists._sql = `EXISTS (SELECT 1 FROM "${as}")`;
1342
+ });
799
1343
  });
800
1344
  if (connectOrCreate) connectOrCreate.values.forEach((array, i) => {
801
1345
  const foreignKeyValues = connectOrCreate.items[i];
802
- for (const value of array) _appendQuery(querySelf, _queryUpsert(rel.where(value.where), {
803
- update: foreignKeyValues,
804
- create: {
805
- ...value.create,
806
- ...foreignKeyValues
1346
+ for (const value of array) {
1347
+ const connectOrCreateWhereExists = querySelf.q.type === "upsert" ? new RawSql("") : void 0;
1348
+ const connectOrCreateSelectAs = {};
1349
+ if (connectOrCreateWhereExists) for (const key of foreignKeys) {
1350
+ const value = foreignKeyValues[key];
1351
+ value.result = { value: rel.qb.shape[key] };
1352
+ connectOrCreateSelectAs[key] = value;
1353
+ }
1354
+ const query = _queryUpsert(rel.where(value.where), {
1355
+ update: foreignKeyValues,
1356
+ create: {
1357
+ ...value.create,
1358
+ ...foreignKeyValues
1359
+ }
1360
+ });
1361
+ if (connectOrCreateWhereExists) {
1362
+ _queryWhere(query, [connectOrCreateWhereExists]);
1363
+ const guardQuery = _queryWhere(_queryTake(_querySelect(_clone(rel.qb), [{ one: new RawSql("1") }])), [connectOrCreateWhereExists]);
1364
+ const sourceQuery = _querySelect(_clone(rel.qb).from(guardQuery), [connectOrCreateSelectAs]);
1365
+ const q = query.q;
1366
+ const columns = q.columns || [];
1367
+ const foreignKeysSet = new Set(foreignKeys);
1368
+ q.columns = [...foreignKeys, ...columns.filter((column) => !foreignKeysSet.has(column))];
1369
+ q.values = q.values.map((row) => row.filter((_, i) => !foreignKeysSet.has(columns[i])));
1370
+ q.queryColumnsCount = foreignKeys.length;
1371
+ q.insertFrom = prepareSubQueryForSql(query, sourceQuery);
807
1372
  }
808
- }), (as) => connectOrCreateAs = as);
1373
+ (querySelf.q.type === "upsert" ? _appendQueryOnUpsertCreate : _appendQuery)(querySelf, query, (as) => {
1374
+ upsertCreateAs = as;
1375
+ setUpsertCreatePlaceholders();
1376
+ connectOrCreateAs = as;
1377
+ if (connectOrCreateWhereExists) connectOrCreateWhereExists._sql = `EXISTS (SELECT 1 FROM "${as}")`;
1378
+ });
1379
+ }
809
1380
  });
810
1381
  } else hasRelationHandleCreate(querySelf, ctx, items, rowIndexes, this.key, this.state.primaryKeys, this.nestedInsert);
811
1382
  }
@@ -826,7 +1397,8 @@ var HasManyVirtualColumn = class extends VirtualColumn {
826
1397
  const makeHasManyMethod = (tableConfig, table, relation, relationName, query) => {
827
1398
  const relPKeys = getPrimaryKeys(query);
828
1399
  if ("through" in relation.options) {
829
- const { through, source } = relation.options;
1400
+ const { through, source, on } = relation.options;
1401
+ if (on) _queryWhere(query, [on]);
830
1402
  const throughRelation = getThroughRelation(table, through);
831
1403
  const sourceRelation = getSourceRelation(throughRelation, source);
832
1404
  const sourceRelationQuery = sourceRelation.query.as(relationName);
@@ -1070,7 +1642,7 @@ const makeHasAndBelongsToManyMethod = (tableConfig, table, qb, relation, relatio
1070
1642
  const primaryKeys = options.columns;
1071
1643
  const originalForeignKeys = options.references;
1072
1644
  const foreignKeys = snakeCase ? [...originalForeignKeys] : originalForeignKeys;
1073
- const joinTable = options.through.table;
1645
+ const joinTable = snakeCase && options.through.snakeCase !== false ? toSnakeCase(options.through.table) : options.through.table;
1074
1646
  const originalThroughForeignKeys = options.through.columns;
1075
1647
  const throughForeignKeys = snakeCase ? [...originalThroughForeignKeys] : originalThroughForeignKeys;
1076
1648
  const throughPrimaryKeys = options.through.references;
@@ -1362,9 +1934,15 @@ const applyRelations = (qb, tables, result, schema) => {
1362
1934
  const dbTable = result[name];
1363
1935
  for (const relationName in table.relations) {
1364
1936
  const relation = table.relations[relationName];
1365
- const otherTableClass = relation.fn();
1366
- const otherTable = tableEntries.find((pair) => pair[1] instanceof otherTableClass);
1367
- if (!otherTable) throw new Error(`Cannot find table class for class ${otherTableClass.name}`);
1937
+ const otherTableClass = relation.fn?.();
1938
+ const otherTable = otherTableClass ? tableEntries.find((pair) => pair[1] instanceof otherTableClass) : tableEntries.find(([, table]) => {
1939
+ const { id } = relation;
1940
+ return table.id === id || table.table === id;
1941
+ }) || tableEntries.find(([, table]) => {
1942
+ const { id } = relation;
1943
+ return table.name === id;
1944
+ });
1945
+ if (!otherTable) throw new Error(otherTableClass ? `Cannot find table class for class ${otherTableClass.name}` : `Cannot find table for relation target ${relation.id}`);
1368
1946
  const otherTableName = otherTable[0];
1369
1947
  const otherDbTable = result[otherTableName];
1370
1948
  if (!otherDbTable) throw new Error(`Cannot find table class by name ${otherTableName}`);
@@ -1690,6 +2268,6 @@ const createRepo = (table, methods) => {
1690
2268
  return q[key];
1691
2269
  } });
1692
2270
  };
1693
- export { bundleOrchidORM, bundleOrchidORMTables, createBaseTable, createRepo, defineRls, makeOrchidOrmDbWithAdapter, orchidORMWithAdapter, setGrants };
2271
+ export { bundleOrchidORM, bundleOrchidORMTables, createBaseTable, createRepo, createTableFactory, defineRls, makeOrchidOrmDbWithAdapter, orchidORMWithAdapter, setGrants };
1694
2272
 
1695
2273
  //# sourceMappingURL=index.mjs.map