@rvoh/dream 0.44.7 → 0.44.9

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.
Files changed (193) hide show
  1. package/dist/cjs/src/bin/index.js +7 -87
  2. package/dist/cjs/src/dream/LeftJoinLoadBuilder.js +1 -1
  3. package/dist/cjs/src/dream/LoadBuilder.js +1 -1
  4. package/dist/cjs/src/dream/Query.js +46 -1417
  5. package/dist/cjs/src/dream/QueryDriver/Base.js +311 -0
  6. package/dist/cjs/src/dream/QueryDriver/Kysely.js +1771 -0
  7. package/dist/cjs/src/dream/QueryDriver/Postgres.js +6 -0
  8. package/dist/cjs/src/dream/internal/saveDream.js +4 -17
  9. package/dist/cjs/src/serializer/SerializerRenderer.js +1 -1
  10. package/dist/esm/src/bin/index.js +7 -87
  11. package/dist/esm/src/dream/LeftJoinLoadBuilder.js +1 -1
  12. package/dist/esm/src/dream/LoadBuilder.js +1 -1
  13. package/dist/esm/src/dream/Query.js +46 -1417
  14. package/dist/esm/src/dream/QueryDriver/Base.js +308 -0
  15. package/dist/esm/src/dream/QueryDriver/Kysely.js +1768 -0
  16. package/dist/esm/src/dream/QueryDriver/Postgres.js +3 -0
  17. package/dist/esm/src/dream/internal/saveDream.js +4 -17
  18. package/dist/esm/src/serializer/SerializerRenderer.js +1 -1
  19. package/dist/types/src/bin/index.d.ts +0 -2
  20. package/dist/types/src/dream/Query.d.ts +27 -155
  21. package/dist/types/src/dream/QueryDriver/Base.d.ts +242 -0
  22. package/dist/types/src/dream/QueryDriver/Kysely.d.ts +354 -0
  23. package/dist/types/src/dream/QueryDriver/Postgres.d.ts +4 -0
  24. package/dist/types/src/dream/internal/executeDatabaseQuery.d.ts +2 -1
  25. package/docs/assets/search.js +1 -1
  26. package/docs/classes/Benchmark.html +2 -2
  27. package/docs/classes/CalendarDate.html +2 -2
  28. package/docs/classes/CreateOrFindByFailedToCreateAndFind.html +3 -3
  29. package/docs/classes/Decorators.html +19 -19
  30. package/docs/classes/Dream.html +131 -131
  31. package/docs/classes/DreamApp.html +4 -4
  32. package/docs/classes/DreamBin.html +3 -4
  33. package/docs/classes/DreamCLI.html +4 -4
  34. package/docs/classes/DreamImporter.html +2 -2
  35. package/docs/classes/DreamLogos.html +2 -2
  36. package/docs/classes/DreamMigrationHelpers.html +7 -7
  37. package/docs/classes/DreamSerializerBuilder.html +2 -2
  38. package/docs/classes/DreamTransaction.html +2 -2
  39. package/docs/classes/Encrypt.html +2 -2
  40. package/docs/classes/Env.html +2 -2
  41. package/docs/classes/GlobalNameNotSet.html +3 -3
  42. package/docs/classes/NonLoadedAssociation.html +3 -3
  43. package/docs/classes/ObjectSerializerBuilder.html +2 -2
  44. package/docs/classes/Query.html +86 -57
  45. package/docs/classes/Range.html +2 -2
  46. package/docs/classes/RecordNotFound.html +3 -3
  47. package/docs/classes/ValidationError.html +3 -3
  48. package/docs/functions/DreamSerializer.html +1 -1
  49. package/docs/functions/ObjectSerializer.html +1 -1
  50. package/docs/functions/ReplicaSafe.html +1 -1
  51. package/docs/functions/STI.html +1 -1
  52. package/docs/functions/SoftDelete.html +1 -1
  53. package/docs/functions/camelize.html +1 -1
  54. package/docs/functions/capitalize.html +1 -1
  55. package/docs/functions/cloneDeepSafe.html +1 -1
  56. package/docs/functions/closeAllDbConnections.html +1 -1
  57. package/docs/functions/compact.html +1 -1
  58. package/docs/functions/dreamDbConnections.html +1 -1
  59. package/docs/functions/dreamPath.html +1 -1
  60. package/docs/functions/expandStiClasses.html +1 -1
  61. package/docs/functions/generateDream.html +1 -1
  62. package/docs/functions/globalClassNameFromFullyQualifiedModelName.html +1 -1
  63. package/docs/functions/groupBy.html +1 -1
  64. package/docs/functions/hyphenize.html +1 -1
  65. package/docs/functions/inferSerializerFromDreamOrViewModel.html +1 -1
  66. package/docs/functions/inferSerializersFromDreamClassOrViewModelClass.html +1 -1
  67. package/docs/functions/intersection.html +1 -1
  68. package/docs/functions/isDreamSerializer.html +1 -1
  69. package/docs/functions/isEmpty.html +1 -1
  70. package/docs/functions/loadRepl.html +1 -1
  71. package/docs/functions/lookupClassByGlobalName.html +1 -1
  72. package/docs/functions/normalizeUnicode.html +1 -1
  73. package/docs/functions/pascalize.html +1 -1
  74. package/docs/functions/pgErrorType.html +1 -1
  75. package/docs/functions/range-1.html +1 -1
  76. package/docs/functions/relativeDreamPath.html +1 -1
  77. package/docs/functions/round.html +1 -1
  78. package/docs/functions/serializerNameFromFullyQualifiedModelName.html +1 -1
  79. package/docs/functions/sharedPathPrefix.html +1 -1
  80. package/docs/functions/snakeify.html +1 -1
  81. package/docs/functions/sort.html +1 -1
  82. package/docs/functions/sortBy.html +1 -1
  83. package/docs/functions/sortObjectByKey.html +1 -1
  84. package/docs/functions/sortObjectByValue.html +1 -1
  85. package/docs/functions/standardizeFullyQualifiedModelName.html +1 -1
  86. package/docs/functions/uncapitalize.html +1 -1
  87. package/docs/functions/uniq.html +1 -1
  88. package/docs/functions/untypedDb.html +1 -1
  89. package/docs/functions/validateColumn.html +1 -1
  90. package/docs/functions/validateTable.html +1 -1
  91. package/docs/interfaces/BelongsToStatement.html +2 -2
  92. package/docs/interfaces/DecoratorContext.html +2 -2
  93. package/docs/interfaces/DreamAppInitOptions.html +2 -2
  94. package/docs/interfaces/DreamAppOpts.html +2 -2
  95. package/docs/interfaces/EncryptOptions.html +2 -2
  96. package/docs/interfaces/InternalAnyTypedSerializerRendersMany.html +2 -2
  97. package/docs/interfaces/InternalAnyTypedSerializerRendersOne.html +2 -2
  98. package/docs/interfaces/OpenapiDescription.html +2 -2
  99. package/docs/interfaces/OpenapiSchemaProperties.html +1 -1
  100. package/docs/interfaces/OpenapiSchemaPropertiesShorthand.html +1 -1
  101. package/docs/interfaces/OpenapiTypeFieldObject.html +1 -1
  102. package/docs/interfaces/SerializerRendererOpts.html +2 -2
  103. package/docs/types/Camelized.html +1 -1
  104. package/docs/types/CommonOpenapiSchemaObjectFields.html +1 -1
  105. package/docs/types/DateTime.html +1 -1
  106. package/docs/types/DbConnectionType.html +1 -1
  107. package/docs/types/DbTypes.html +1 -1
  108. package/docs/types/DreamAssociationMetadata.html +1 -1
  109. package/docs/types/DreamAttributes.html +1 -1
  110. package/docs/types/DreamClassColumn.html +1 -1
  111. package/docs/types/DreamColumn.html +1 -1
  112. package/docs/types/DreamColumnNames.html +1 -1
  113. package/docs/types/DreamLogLevel.html +1 -1
  114. package/docs/types/DreamLogger.html +1 -1
  115. package/docs/types/DreamModelSerializerType.html +1 -1
  116. package/docs/types/DreamOrViewModelClassSerializerKey.html +1 -1
  117. package/docs/types/DreamOrViewModelSerializerKey.html +1 -1
  118. package/docs/types/DreamParamSafeAttributes.html +1 -1
  119. package/docs/types/DreamParamSafeColumnNames.html +1 -1
  120. package/docs/types/DreamSerializable.html +1 -1
  121. package/docs/types/DreamSerializableArray.html +1 -1
  122. package/docs/types/DreamSerializerKey.html +1 -1
  123. package/docs/types/DreamSerializers.html +1 -1
  124. package/docs/types/DreamTableSchema.html +1 -1
  125. package/docs/types/DreamVirtualColumns.html +1 -1
  126. package/docs/types/EncryptAlgorithm.html +1 -1
  127. package/docs/types/HasManyStatement.html +1 -1
  128. package/docs/types/HasOneStatement.html +1 -1
  129. package/docs/types/Hyphenized.html +1 -1
  130. package/docs/types/IdType.html +1 -1
  131. package/docs/types/OpenapiAllTypes.html +1 -1
  132. package/docs/types/OpenapiFormats.html +1 -1
  133. package/docs/types/OpenapiNumberFormats.html +1 -1
  134. package/docs/types/OpenapiPrimitiveBaseTypes.html +1 -1
  135. package/docs/types/OpenapiPrimitiveTypes.html +1 -1
  136. package/docs/types/OpenapiSchemaArray.html +1 -1
  137. package/docs/types/OpenapiSchemaArrayShorthand.html +1 -1
  138. package/docs/types/OpenapiSchemaBase.html +1 -1
  139. package/docs/types/OpenapiSchemaBody.html +1 -1
  140. package/docs/types/OpenapiSchemaBodyShorthand.html +1 -1
  141. package/docs/types/OpenapiSchemaCommonFields.html +1 -1
  142. package/docs/types/OpenapiSchemaExpressionAllOf.html +1 -1
  143. package/docs/types/OpenapiSchemaExpressionAnyOf.html +1 -1
  144. package/docs/types/OpenapiSchemaExpressionOneOf.html +1 -1
  145. package/docs/types/OpenapiSchemaExpressionRef.html +1 -1
  146. package/docs/types/OpenapiSchemaExpressionRefSchemaShorthand.html +1 -1
  147. package/docs/types/OpenapiSchemaInteger.html +1 -1
  148. package/docs/types/OpenapiSchemaNull.html +1 -1
  149. package/docs/types/OpenapiSchemaNumber.html +1 -1
  150. package/docs/types/OpenapiSchemaObject.html +1 -1
  151. package/docs/types/OpenapiSchemaObjectAllOf.html +1 -1
  152. package/docs/types/OpenapiSchemaObjectAllOfShorthand.html +1 -1
  153. package/docs/types/OpenapiSchemaObjectAnyOf.html +1 -1
  154. package/docs/types/OpenapiSchemaObjectAnyOfShorthand.html +1 -1
  155. package/docs/types/OpenapiSchemaObjectBase.html +1 -1
  156. package/docs/types/OpenapiSchemaObjectBaseShorthand.html +1 -1
  157. package/docs/types/OpenapiSchemaObjectOneOf.html +1 -1
  158. package/docs/types/OpenapiSchemaObjectOneOfShorthand.html +1 -1
  159. package/docs/types/OpenapiSchemaObjectShorthand.html +1 -1
  160. package/docs/types/OpenapiSchemaPrimitiveGeneric.html +1 -1
  161. package/docs/types/OpenapiSchemaShorthandExpressionAllOf.html +1 -1
  162. package/docs/types/OpenapiSchemaShorthandExpressionAnyOf.html +1 -1
  163. package/docs/types/OpenapiSchemaShorthandExpressionOneOf.html +1 -1
  164. package/docs/types/OpenapiSchemaShorthandExpressionSerializableRef.html +1 -1
  165. package/docs/types/OpenapiSchemaShorthandExpressionSerializerRef.html +1 -1
  166. package/docs/types/OpenapiSchemaShorthandPrimitiveGeneric.html +1 -1
  167. package/docs/types/OpenapiSchemaString.html +1 -1
  168. package/docs/types/OpenapiShorthandAllTypes.html +1 -1
  169. package/docs/types/OpenapiShorthandPrimitiveBaseTypes.html +1 -1
  170. package/docs/types/OpenapiShorthandPrimitiveTypes.html +1 -1
  171. package/docs/types/OpenapiTypeField.html +1 -1
  172. package/docs/types/Pascalized.html +1 -1
  173. package/docs/types/PrimaryKeyType.html +1 -1
  174. package/docs/types/RoundingPrecision.html +1 -1
  175. package/docs/types/SerializerCasing.html +1 -1
  176. package/docs/types/SimpleObjectSerializerType.html +1 -1
  177. package/docs/types/Snakeified.html +1 -1
  178. package/docs/types/Timestamp.html +1 -1
  179. package/docs/types/UpdateableAssociationProperties.html +1 -1
  180. package/docs/types/UpdateableProperties.html +1 -1
  181. package/docs/types/ValidationType.html +1 -1
  182. package/docs/types/ViewModel.html +1 -1
  183. package/docs/types/ViewModelClass.html +1 -1
  184. package/docs/types/WhereStatementForDream.html +1 -1
  185. package/docs/types/WhereStatementForDreamClass.html +1 -1
  186. package/docs/variables/DateTime-1.html +1 -1
  187. package/docs/variables/DreamConst.html +1 -1
  188. package/docs/variables/TRIGRAM_OPERATORS.html +1 -1
  189. package/docs/variables/openapiPrimitiveTypes-1.html +1 -1
  190. package/docs/variables/openapiShorthandPrimitiveTypes-1.html +1 -1
  191. package/docs/variables/ops.html +1 -1
  192. package/docs/variables/primaryKeyTypes.html +1 -1
  193. package/package.json +1 -1
@@ -1,21 +1,8 @@
1
- import { sql, } from 'kysely';
2
- import pluralize from 'pluralize-esm';
3
- import ConnectedToDB from '../db/ConnectedToDB.js';
4
1
  import { SOFT_DELETE_SCOPE_NAME } from '../decorators/class/SoftDelete.js';
5
- import associationToGetterSetterProp from '../decorators/field/association/associationToGetterSetterProp.js';
6
2
  import DreamApp from '../dream-app/index.js';
7
3
  import AssociationDeclaredWithoutAssociatedDreamClass from '../errors/associations/AssociationDeclaredWithoutAssociatedDreamClass.js';
8
- import CannotAssociateThroughPolymorphic from '../errors/associations/CannotAssociateThroughPolymorphic.js';
9
- import CannotJoinPolymorphicBelongsToError from '../errors/associations/CannotJoinPolymorphicBelongsToError.js';
10
- import JoinAttemptedOnMissingAssociation from '../errors/associations/JoinAttemptedOnMissingAssociation.js';
11
- import MissingRequiredAssociationAndClause from '../errors/associations/MissingRequiredAssociationAndClause.js';
12
- import MissingRequiredPassthroughForAssociationAndClause from '../errors/associations/MissingRequiredPassthroughForAssociationAndClause.js';
13
- import MissingThroughAssociation from '../errors/associations/MissingThroughAssociation.js';
14
- import MissingThroughAssociationSource from '../errors/associations/MissingThroughAssociationSource.js';
15
4
  import CannotCallUndestroyOnANonSoftDeleteModel from '../errors/CannotCallUndestroyOnANonSoftDeleteModel.js';
16
- import CannotNegateSimilarityClause from '../errors/CannotNegateSimilarityClause.js';
17
5
  import CannotPassAdditionalFieldsToPluckEachAfterCallback from '../errors/CannotPassAdditionalFieldsToPluckEachAfterCallback.js';
18
- import CannotPassUndefinedAsAValueToAWhereClause from '../errors/CannotPassUndefinedAsAValueToAWhereClause.js';
19
6
  import LeftJoinPreloadIncompatibleWithFindEach from '../errors/LeftJoinPreloadIncompatibleWithFindEach.js';
20
7
  import MissingRequiredCallbackFunctionToPluckEach from '../errors/MissingRequiredCallbackFunctionToPluckEach.js';
21
8
  import NoUpdateAllOnJoins from '../errors/NoUpdateAllOnJoins.js';
@@ -23,33 +10,14 @@ import NoUpdateOnAssociationQuery from '../errors/NoUpdateOnAssociationQuery.js'
23
10
  import CannotPaginateWithLimit from '../errors/pagination/CannotPaginateWithLimit.js';
24
11
  import CannotPaginateWithOffset from '../errors/pagination/CannotPaginateWithOffset.js';
25
12
  import RecordNotFound from '../errors/RecordNotFound.js';
26
- import UnexpectedUndefined from '../errors/UnexpectedUndefined.js';
27
- import CalendarDate from '../helpers/CalendarDate.js';
28
- import camelize from '../helpers/camelize.js';
29
13
  import cloneDeepSafe from '../helpers/cloneDeepSafe.js';
30
- import compact from '../helpers/compact.js';
31
- import { DateTime } from '../helpers/DateTime.js';
32
- import isEmpty from '../helpers/isEmpty.js';
33
14
  import isObject from '../helpers/isObject.js';
34
15
  import namespaceColumn from '../helpers/namespaceColumn.js';
35
- import normalizeUnicode from '../helpers/normalizeUnicode.js';
36
- import objectPathsToArrays from '../helpers/objectPathsToArrays.js';
37
16
  import protectAgainstPollutingAssignment from '../helpers/protectAgainstPollutingAssignment.js';
38
- import { Range } from '../helpers/range.js';
39
- import snakeify from '../helpers/snakeify.js';
40
- import uniq from '../helpers/uniq.js';
41
- import CurriedOpsStatement from '../ops/curried-ops-statement.js';
42
17
  import ops from '../ops/index.js';
43
- import OpsStatement from '../ops/ops-statement.js';
44
- import { DreamConst } from './constants.js';
45
18
  import computedPaginatePage from './internal/computedPaginatePage.js';
46
- import executeDatabaseQuery from './internal/executeDatabaseQuery.js';
47
- import extractAssociationMetadataFromAssociationName from './internal/extractAssociationMetadataFromAssociationName.js';
48
- import orderByDirection from './internal/orderByDirection.js';
49
- import shouldBypassDefaultScope from './internal/shouldBypassDefaultScope.js';
50
- import SimilarityBuilder from './internal/similarity/SimilarityBuilder.js';
51
- import sqlResultToDreamInstance from './internal/sqlResultToDreamInstance.js';
52
- export default class Query extends ConnectedToDB {
19
+ import PostgresQueryDriver from './QueryDriver/Postgres.js';
20
+ export default class Query {
53
21
  /**
54
22
  * @internal
55
23
  *
@@ -228,8 +196,26 @@ export default class Query extends ConnectedToDB {
228
196
  * baseSelectQuery.
229
197
  */
230
198
  baseSelectQuery;
199
+ /*
200
+ * Store the original opts, so we can hand them off succinctly to the
201
+ * query executor. TODO: Should we do this?
202
+ * */
203
+ originalOpts;
204
+ dreamClass;
205
+ dreamInstance;
206
+ connectionOverride;
207
+ /**
208
+ * @internal
209
+ *
210
+ * stores the Dream models joined in this Query instance
211
+ */
212
+ innerJoinDreamClasses = Object.freeze([]);
231
213
  constructor(dreamInstance, opts = {}) {
232
- super(dreamInstance, opts);
214
+ this.dreamInstance = dreamInstance;
215
+ this.dreamClass = dreamInstance.constructor;
216
+ this.dreamTransaction = opts.transaction || null;
217
+ this.connectionOverride = opts.connection;
218
+ this.innerJoinDreamClasses = Object.freeze(opts.innerJoinDreamClasses || []);
233
219
  this.passthroughOnStatement = Object.freeze(opts.passthroughOnStatement || {});
234
220
  this.whereStatements = Object.freeze(opts.where || []);
235
221
  this.whereNotStatements = Object.freeze(opts.whereNot || []);
@@ -254,6 +240,7 @@ export default class Query extends ConnectedToDB {
254
240
  this.distinctColumn = opts.distinctColumn || null;
255
241
  this.connectionOverride = opts.connection;
256
242
  this.shouldReallyDestroy = opts.shouldReallyDestroy || false;
243
+ this.originalOpts = Object.freeze(opts);
257
244
  }
258
245
  /**
259
246
  * Returns true. Useful for distinguishing Query instances
@@ -264,23 +251,6 @@ export default class Query extends ConnectedToDB {
264
251
  get isDreamQuery() {
265
252
  return true;
266
253
  }
267
- /**
268
- * @internal
269
- *
270
- * Used for applying preload and load statements
271
- *
272
- * @returns An associated Query
273
- */
274
- dreamClassQueryWithScopeBypasses(dreamClass, { bypassAllDefaultScopesExceptOnAssociations = false, defaultScopesToBypassExceptOnAssociations = [], } = {}) {
275
- const associationQuery = dreamClass.query().clone({
276
- passthroughOnStatement: this.passthroughOnStatement,
277
- bypassAllDefaultScopes: this.bypassAllDefaultScopes,
278
- bypassAllDefaultScopesExceptOnAssociations,
279
- defaultScopesToBypass: this.defaultScopesToBypass,
280
- defaultScopesToBypassExceptOnAssociations,
281
- });
282
- return (this.dreamTransaction ? associationQuery.txn(this.dreamTransaction) : associationQuery);
283
- }
284
254
  /**
285
255
  * @internal
286
256
  *
@@ -580,92 +550,6 @@ export default class Query extends ConnectedToDB {
580
550
  this.fleshOutJoinStatements(innerJoinDreamClasses, joinStatements, joinAndStatements, previousAssociationName, associationStatements, previousDreamClass);
581
551
  }
582
552
  }
583
- /**
584
- * @internal
585
- *
586
- *
587
- */
588
- associationNamesToDreamClassesMap(associationNames, associationsToDreamClassesMap = {}) {
589
- const namesToAssociationsAndDreamClasses = this.associationNamesToAssociationDataAndDreamClassesMap(associationNames);
590
- return Object.keys(namesToAssociationsAndDreamClasses).reduce((remap, associationName) => {
591
- const associationAndDreamClass = namesToAssociationsAndDreamClasses[associationName];
592
- if (associationAndDreamClass === undefined)
593
- throw new UnexpectedUndefined();
594
- remap[associationName] = associationAndDreamClass.dreamClass;
595
- return remap;
596
- }, associationsToDreamClassesMap);
597
- }
598
- /**
599
- * @internal
600
- *
601
- *
602
- */
603
- associationNamesToAssociationsMap(associationNames, associationsToAssociations = {}) {
604
- const namesToAssociationsAndDreamClasses = this.associationNamesToAssociationDataAndDreamClassesMap(associationNames);
605
- return Object.keys(namesToAssociationsAndDreamClasses).reduce((remap, associationName) => {
606
- const associationAndDreamClass = namesToAssociationsAndDreamClasses[associationName];
607
- if (associationAndDreamClass === undefined)
608
- throw new UnexpectedUndefined();
609
- remap[associationName] = associationAndDreamClass.association;
610
- return remap;
611
- }, associationsToAssociations);
612
- }
613
- /**
614
- * @internal
615
- */
616
- associationNamesToAssociationDataAndDreamClassesMap(associationNames) {
617
- const associationsToDreamClassesMap = {};
618
- associationNames.reduce((dreamClass, associationName) => {
619
- const { name, alias } = extractAssociationMetadataFromAssociationName(associationName);
620
- const association = dreamClass['getAssociationMetadata'](name);
621
- if (association === undefined)
622
- throw new UnexpectedUndefined();
623
- const through = association.through;
624
- if (through) {
625
- const { throughAssociation, throughAssociationDreamClass } = this.throughAssociationDetails(dreamClass, through);
626
- associationsToDreamClassesMap[through] = {
627
- association: throughAssociation,
628
- dreamClass: throughAssociationDreamClass,
629
- };
630
- }
631
- const nextDreamClass = association.modelCB();
632
- if (alias === undefined)
633
- throw new UnexpectedUndefined();
634
- associationsToDreamClassesMap[alias] = { association, dreamClass: nextDreamClass };
635
- return nextDreamClass;
636
- }, this.dreamClass);
637
- return associationsToDreamClassesMap;
638
- }
639
- /**
640
- * @internal
641
- */
642
- throughAssociationDetails(dreamClass, through) {
643
- const throughAssociation = dreamClass['getAssociationMetadata'](through);
644
- if (throughAssociation === undefined)
645
- throw new UnexpectedUndefined();
646
- const throughAssociationDreamClass = throughAssociation.modelCB();
647
- return { throughAssociation, throughAssociationDreamClass };
648
- }
649
- /**
650
- * @internal
651
- *
652
- *
653
- */
654
- joinStatementsToDreamClassesMap(joinStatements) {
655
- const associationsToDreamClassesMap = {};
656
- objectPathsToArrays(joinStatements).forEach(associationChain => this.associationNamesToDreamClassesMap(associationChain, associationsToDreamClassesMap));
657
- return associationsToDreamClassesMap;
658
- }
659
- /**
660
- * @internal
661
- *
662
- *
663
- */
664
- joinStatementsToAssociationsMap(joinStatements) {
665
- const associationsToAssociationsMap = {};
666
- objectPathsToArrays(joinStatements).forEach(associationChain => this.associationNamesToAssociationsMap(associationChain, associationsToAssociationsMap));
667
- return associationsToAssociationsMap;
668
- }
669
553
  /**
670
554
  * @internal
671
555
  *
@@ -883,8 +767,7 @@ export default class Query extends ConnectedToDB {
883
767
  * @returns A Kysely SelectQueryBuilder instance
884
768
  */
885
769
  nestedSelect(selection) {
886
- const query = this.buildSelect({ bypassSelectAll: true, bypassOrder: true });
887
- return query.select(this.namespaceColumn(selection));
770
+ return this.dbDriverInstance().nestedSelect(selection);
888
771
  }
889
772
  /**
890
773
  * Returns a new Query instance, attaching the provided
@@ -977,8 +860,7 @@ export default class Query extends ConnectedToDB {
977
860
  *
978
861
  */
979
862
  sql() {
980
- const kyselyQuery = this.buildSelect();
981
- return kyselyQuery.compile();
863
+ return this.dbDriverInstance().sql();
982
864
  }
983
865
  /**
984
866
  * Converts the given dream class into a Kysely query, enabling
@@ -992,21 +874,7 @@ export default class Query extends ConnectedToDB {
992
874
  * @returns A Kysely query. Depending on the type passed, it will return either a SelectQueryBuilder, DeleteQueryBuilder, or an UpdateQueryBuilder
993
875
  */
994
876
  toKysely(type) {
995
- switch (type) {
996
- case 'select':
997
- return this.buildSelect();
998
- case 'delete':
999
- return this.buildDelete();
1000
- case 'update':
1001
- return this.buildUpdate({});
1002
- // TODO: in the future, we should support insert type, but don't yet, since inserts are done outside
1003
- // the query class for some reason.
1004
- default: {
1005
- // protection so that if a new QueryType is ever added, this will throw a type error at build time
1006
- const _never = type;
1007
- throw new Error(`Unhandled QueryType: ${_never}`);
1008
- }
1009
- }
877
+ return this.dbDriverInstance().toKysely(type);
1010
878
  }
1011
879
  /**
1012
880
  * Applies transaction to the Query instance
@@ -1034,17 +902,7 @@ export default class Query extends ConnectedToDB {
1034
902
  * @returns The number of records in the database
1035
903
  */
1036
904
  async count() {
1037
- // eslint-disable-next-line @typescript-eslint/unbound-method
1038
- const { count } = this.dbFor('select').fn;
1039
- const distinctColumn = this.distinctColumn;
1040
- const query = this.clone({ distinctColumn: null });
1041
- let kyselyQuery = query.buildSelect({ bypassSelectAll: true, bypassOrder: true });
1042
- const countClause = distinctColumn
1043
- ? count(sql `DISTINCT ${distinctColumn}`)
1044
- : count(query.namespaceColumn(query.dreamInstance.primaryKey));
1045
- kyselyQuery = kyselyQuery.select(countClause.as('tablecount'));
1046
- const data = await executeDatabaseQuery(kyselyQuery, 'executeTakeFirstOrThrow');
1047
- return parseInt(data.tablecount.toString());
905
+ return await this.dbDriverInstance().count();
1048
906
  }
1049
907
  /**
1050
908
  * Returns new Query with distinct clause applied
@@ -1092,12 +950,7 @@ export default class Query extends ConnectedToDB {
1092
950
  *
1093
951
  */
1094
952
  async max(columnName) {
1095
- // eslint-disable-next-line @typescript-eslint/unbound-method
1096
- const { max } = this.dbFor('select').fn;
1097
- let kyselyQuery = this.buildSelect({ bypassSelectAll: true, bypassOrder: true });
1098
- kyselyQuery = kyselyQuery.select(max(columnName));
1099
- const data = await executeDatabaseQuery(kyselyQuery, 'executeTakeFirstOrThrow');
1100
- return data.max;
953
+ return await this.dbDriverInstance().max(columnName);
1101
954
  }
1102
955
  /**
1103
956
  * Retrieves the min value of the specified column
@@ -1112,218 +965,7 @@ export default class Query extends ConnectedToDB {
1112
965
  * @returns the min value of the specified column for this Query
1113
966
  */
1114
967
  async min(columnName) {
1115
- // eslint-disable-next-line @typescript-eslint/unbound-method
1116
- const { min } = this.dbFor('select').fn;
1117
- let kyselyQuery = this.buildSelect({ bypassSelectAll: true, bypassOrder: true });
1118
- kyselyQuery = kyselyQuery.select(min(columnName));
1119
- const data = await executeDatabaseQuery(kyselyQuery, 'executeTakeFirstOrThrow');
1120
- return data.min;
1121
- }
1122
- /**
1123
- * @internal
1124
- *
1125
- * Runs the query and extracts plucked values
1126
- *
1127
- * @returns An array of plucked values
1128
- */
1129
- async executePluck(...fields) {
1130
- let kyselyQuery = this.removeAllDefaultScopesExceptOnAssociations().buildSelect({ bypassSelectAll: true });
1131
- const aliases = [];
1132
- fields.forEach((field) => {
1133
- // field will already be namespaced in a join situation, but when the field to pluck is on the
1134
- // base model, it will be underscored (to match the table name), but when the selected column
1135
- // comes back from Kysely camelCased
1136
- aliases.push(field.includes('_') ? camelize(field) : field);
1137
- // namespace the selection so that when plucking the same column name from
1138
- // multpile tables, they don't get saved as the same name (e.g. select results with two `id` columns,
1139
- // which the pg package then returns in an object with a single `id` key)
1140
- kyselyQuery = kyselyQuery.select(`${this.namespaceColumn(field)} as ${field}`);
1141
- });
1142
- return (await executeDatabaseQuery(kyselyQuery, 'execute')).map(singleResult => aliases.map(alias => singleResult[alias]));
1143
- }
1144
- /**
1145
- * @internal
1146
- *
1147
- */
1148
- async executeJoinLoad(options = {}) {
1149
- const query = this.limit(null).offset(null);
1150
- let kyselyQuery = query.buildSelect({ bypassSelectAll: true });
1151
- const aliasToDreamClassesMap = {
1152
- [this.baseSqlAlias]: this.dreamClass,
1153
- ...this.joinStatementsToDreamClassesMap(this.leftJoinStatements),
1154
- };
1155
- const associationAliasToColumnAliasMap = {};
1156
- const aliasToAssociationsMap = this.joinStatementsToAssociationsMap(this.leftJoinStatements);
1157
- const aliases = Object.keys(aliasToDreamClassesMap);
1158
- let nextColumnAliasCounter = 0;
1159
- aliases.forEach((aliasOrExpression) => {
1160
- const alias = extractAssociationMetadataFromAssociationName(aliasOrExpression).alias;
1161
- if (alias === undefined)
1162
- throw new UnexpectedUndefined();
1163
- associationAliasToColumnAliasMap[alias] ||= {};
1164
- const aliasedDreamClass = aliasToDreamClassesMap[alias];
1165
- if (aliasedDreamClass === undefined)
1166
- throw new UnexpectedUndefined();
1167
- const association = aliasToAssociationsMap[alias];
1168
- const columns = alias === this.baseSqlAlias
1169
- ? options.columns
1170
- ? this.columnsWithRequiredLoadColumns(options.columns)
1171
- : this.dreamClass.columns()
1172
- : aliasedDreamClass.columns();
1173
- columns.forEach((column) => {
1174
- const columnAlias = `dr${nextColumnAliasCounter++}`;
1175
- kyselyQuery = kyselyQuery.select(`${this.namespaceColumn(column, alias)} as ${columnAlias}`);
1176
- const columnAliasMap = associationAliasToColumnAliasMap[alias];
1177
- if (columnAliasMap === undefined)
1178
- throw new UnexpectedUndefined();
1179
- columnAliasMap[column] = columnAlias;
1180
- });
1181
- if (association?.type === 'HasOne' || association?.type === 'HasMany') {
1182
- const setupPreloadData = (dbColumnName) => {
1183
- const columnAlias = `dr${nextColumnAliasCounter++}`;
1184
- const columnAliasMap = associationAliasToColumnAliasMap[association.through];
1185
- if (columnAliasMap === undefined)
1186
- throw new UnexpectedUndefined();
1187
- columnAliasMap[dbColumnName] = columnAlias;
1188
- kyselyQuery = kyselyQuery.select(`${this.namespaceColumn(dbColumnName, association.through)} as ${columnAlias}`);
1189
- };
1190
- if (association.through && association.preloadThroughColumns) {
1191
- if (isObject(association.preloadThroughColumns)) {
1192
- const preloadMap = association.preloadThroughColumns;
1193
- Object.keys(preloadMap).forEach(columnName => setupPreloadData(columnName));
1194
- }
1195
- else {
1196
- const preloadArray = association.preloadThroughColumns;
1197
- preloadArray.forEach(columnName => setupPreloadData(columnName));
1198
- }
1199
- }
1200
- }
1201
- });
1202
- const queryResults = await executeDatabaseQuery(kyselyQuery, 'execute');
1203
- const aliasToDreamIdMap = queryResults.reduce((aliasToDreamIdMap, singleSqlResult) => {
1204
- this.fleshOutJoinLoadExecutionResults({
1205
- currentAlias: this.baseSqlAlias,
1206
- singleSqlResult,
1207
- aliasToDreamIdMap,
1208
- associationAliasToColumnAliasMap,
1209
- aliasToAssociationsMap,
1210
- aliasToDreamClassesMap,
1211
- leftJoinStatements: this.leftJoinStatements,
1212
- });
1213
- return aliasToDreamIdMap;
1214
- }, {});
1215
- const baseModelIdToDreamMap = aliasToDreamIdMap[this.baseSqlAlias] || new Map();
1216
- return compact(Array.from(baseModelIdToDreamMap.values()));
1217
- }
1218
- fleshOutJoinLoadExecutionResults({ currentAlias, singleSqlResult, aliasToDreamIdMap, associationAliasToColumnAliasMap, aliasToAssociationsMap, aliasToDreamClassesMap, leftJoinStatements, }) {
1219
- const dreamClass = aliasToDreamClassesMap[currentAlias];
1220
- if (dreamClass === undefined)
1221
- throw new UnexpectedUndefined();
1222
- const columnToColumnAliasMap = associationAliasToColumnAliasMap[currentAlias];
1223
- if (columnToColumnAliasMap === undefined)
1224
- throw new UnexpectedUndefined();
1225
- const primaryKeyName = dreamClass.primaryKey;
1226
- if (primaryKeyName === undefined)
1227
- throw new UnexpectedUndefined();
1228
- const columnAlias = columnToColumnAliasMap[primaryKeyName];
1229
- if (columnAlias === undefined)
1230
- throw new UnexpectedUndefined();
1231
- const primaryKeyValue = singleSqlResult[columnAlias];
1232
- if (!primaryKeyValue)
1233
- return null;
1234
- aliasToDreamIdMap[currentAlias] ||= new Map();
1235
- if (!aliasToDreamIdMap[currentAlias].get(primaryKeyValue)) {
1236
- const columnValueMap = Object.keys(columnToColumnAliasMap).reduce((columnNameValueMap, columnName) => {
1237
- const columnAlias = columnToColumnAliasMap[columnName];
1238
- if (columnAlias === undefined)
1239
- throw new UnexpectedUndefined();
1240
- columnNameValueMap[columnName] = singleSqlResult[columnAlias];
1241
- return columnNameValueMap;
1242
- }, {});
1243
- const dream = sqlResultToDreamInstance(dreamClass, columnValueMap);
1244
- const association = aliasToAssociationsMap[currentAlias];
1245
- if (association && association.through && association.preloadThroughColumns) {
1246
- const throughAssociationColumnToColumnAliasMap = associationAliasToColumnAliasMap[association.through];
1247
- if (throughAssociationColumnToColumnAliasMap === undefined)
1248
- throw new UnexpectedUndefined();
1249
- this.hydratePreloadedThroughColumns({
1250
- association,
1251
- columnToColumnAliasMap: throughAssociationColumnToColumnAliasMap,
1252
- dream,
1253
- singleSqlResult,
1254
- });
1255
- }
1256
- aliasToDreamIdMap[protectAgainstPollutingAssignment(currentAlias)]?.set(primaryKeyValue, dream);
1257
- }
1258
- const dream = aliasToDreamIdMap[currentAlias].get(primaryKeyValue);
1259
- Object.keys(leftJoinStatements).forEach(nextAlias => {
1260
- const { name: associationName, alias } = extractAssociationMetadataFromAssociationName(nextAlias);
1261
- const association = dreamClass['getAssociationMetadata'](associationName);
1262
- if (association === undefined)
1263
- throw new UnexpectedUndefined();
1264
- const associatedDream = this.fleshOutJoinLoadExecutionResults({
1265
- currentAlias: alias,
1266
- singleSqlResult,
1267
- aliasToDreamIdMap,
1268
- associationAliasToColumnAliasMap,
1269
- aliasToAssociationsMap,
1270
- aliasToDreamClassesMap,
1271
- leftJoinStatements: leftJoinStatements[nextAlias],
1272
- });
1273
- const hasMany = association.type === 'HasMany';
1274
- // initialize by trying to access the association, which throws an exception if not yet initialized
1275
- try {
1276
- ;
1277
- dream[association.as];
1278
- }
1279
- catch {
1280
- if (hasMany)
1281
- dream[association.as] = [];
1282
- else
1283
- dream[associationToGetterSetterProp(association)] = null;
1284
- }
1285
- if (!associatedDream)
1286
- return;
1287
- if (hasMany) {
1288
- if (!dream[association.as].includes(associatedDream))
1289
- dream[association.as].push(associatedDream);
1290
- }
1291
- else
1292
- dream[associationToGetterSetterProp(association)] = associatedDream;
1293
- });
1294
- return dream;
1295
- }
1296
- hydratePreloadedThroughColumns({ association, columnToColumnAliasMap, dream, singleSqlResult, }) {
1297
- if (!association.through)
1298
- return;
1299
- if (!dream.preloadedThroughColumns)
1300
- return;
1301
- let columnNames = [];
1302
- const columnNameToPreloadedThroughColumnNameMap = {};
1303
- if (isObject(association.preloadThroughColumns)) {
1304
- const preloadMap = association.preloadThroughColumns;
1305
- columnNames = Object.keys(preloadMap).map(columnName => {
1306
- columnNameToPreloadedThroughColumnNameMap[columnName] = preloadMap[columnName];
1307
- return columnName;
1308
- });
1309
- }
1310
- else if (Array.isArray(association.preloadThroughColumns)) {
1311
- columnNames = association.preloadThroughColumns.map(columnName => {
1312
- columnNameToPreloadedThroughColumnNameMap[columnName] = columnName;
1313
- return columnName;
1314
- });
1315
- }
1316
- columnNames.forEach(columnName => {
1317
- const preloadedThroughColumnName = columnNameToPreloadedThroughColumnNameMap[columnName];
1318
- if (preloadedThroughColumnName === undefined)
1319
- throw new UnexpectedUndefined();
1320
- const columnAlias = columnToColumnAliasMap[columnName];
1321
- if (columnAlias === undefined) {
1322
- throw new UnexpectedUndefined();
1323
- }
1324
- ;
1325
- dream.preloadedThroughColumns[preloadedThroughColumnName] = singleSqlResult[columnAlias];
1326
- });
968
+ return await this.dbDriverInstance().min(columnName);
1327
969
  }
1328
970
  /**
1329
971
  * Plucks the provided fields from the given dream class table
@@ -1345,7 +987,7 @@ export default class Query extends ConnectedToDB {
1345
987
  * @returns An array of pluck results
1346
988
  */
1347
989
  async pluck(...columnNames) {
1348
- const vals = await this.executePluck(...columnNames);
990
+ const vals = await this.dbDriverInstance().pluck(...columnNames);
1349
991
  return (columnNames.length > 1 ? vals : vals.flat());
1350
992
  }
1351
993
  /**
@@ -1382,11 +1024,11 @@ export default class Query extends ConnectedToDB {
1382
1024
  const columnsIncludingPrimaryKey = onlyIncludesPrimaryKey
1383
1025
  ? onlyColumns
1384
1026
  : [this.namespacedPrimaryKey, ...onlyColumns];
1385
- records = await this.offset(offset)
1027
+ const query = this.offset(offset)
1386
1028
  .order(null)
1387
1029
  .order(this.namespacedPrimaryKey)
1388
- .limit(batchSize)
1389
- .executePluck(...columnsIncludingPrimaryKey);
1030
+ .limit(batchSize);
1031
+ records = await this.dbDriverInstance(query).pluck(...columnsIncludingPrimaryKey);
1390
1032
  // In order to batch, we need to order by primary key, so the primary key must be plucked.
1391
1033
  // If the developer did not include the primary key in the columns to pluck, then we prepended it above
1392
1034
  // and need to remove it from each group of plucked columns prior to passing them into the callback functions
@@ -1411,13 +1053,14 @@ export default class Query extends ConnectedToDB {
1411
1053
  * @returns an array of dreams
1412
1054
  */
1413
1055
  async all(options = {}) {
1414
- if (this.joinLoadActivated)
1415
- return await this.executeJoinLoad(options);
1416
- const kyselyQuery = this.buildSelect(options);
1417
- const results = await executeDatabaseQuery(kyselyQuery, 'execute');
1418
- const theAll = results.map(r => sqlResultToDreamInstance(this.dreamClass, r));
1419
- await this.applyPreload(this.preloadStatements, this.preloadOnStatements, theAll);
1420
- return theAll;
1056
+ return await this.dbDriverInstance().takeAll(options);
1057
+ }
1058
+ static dbDriverClass() {
1059
+ return PostgresQueryDriver;
1060
+ }
1061
+ dbDriverInstance(query = this) {
1062
+ const driverClass = Query.dbDriverClass();
1063
+ return new driverClass(query);
1421
1064
  }
1422
1065
  /**
1423
1066
  * Paginates the results of your query, accepting a pageSize and page argument,
@@ -1522,7 +1165,8 @@ export default class Query extends ConnectedToDB {
1522
1165
  const query = this.orderStatements.length
1523
1166
  ? this
1524
1167
  : this.order({ [this.namespacedPrimaryKey]: 'asc' });
1525
- return await query.takeOne();
1168
+ const dbDriverClass = Query.dbDriverClass();
1169
+ return await new dbDriverClass(query).takeOne();
1526
1170
  }
1527
1171
  /**
1528
1172
  * Returns the first record in the database
@@ -1561,7 +1205,8 @@ export default class Query extends ConnectedToDB {
1561
1205
  const query = this.orderStatements.length
1562
1206
  ? this.invertOrder()
1563
1207
  : this.order({ [this.namespacedPrimaryKey]: 'desc' });
1564
- return await query.takeOne();
1208
+ const dbDriverClass = Query.dbDriverClass();
1209
+ return await new dbDriverClass(query).takeOne();
1565
1210
  }
1566
1211
  /**
1567
1212
  * Returns the last record in the database
@@ -1711,8 +1356,7 @@ export default class Query extends ConnectedToDB {
1711
1356
  * @returns The number of records that were removed
1712
1357
  */
1713
1358
  async delete() {
1714
- const deletionResult = await executeDatabaseQuery(this.buildDelete(), 'executeTakeFirst');
1715
- return Number(deletionResult?.numDeletedRows || 0);
1359
+ return await this.dbDriverInstance().delete();
1716
1360
  }
1717
1361
  /**
1718
1362
  * Updates all records matching the Query
@@ -1746,1014 +1390,7 @@ export default class Query extends ConnectedToDB {
1746
1390
  return counter;
1747
1391
  }
1748
1392
  async updateWithoutCallingModelHooks(attributes) {
1749
- const kyselyQuery = this.buildUpdate(attributes);
1750
- const res = await executeDatabaseQuery(kyselyQuery, 'execute');
1751
- const resultData = Array.from(res.entries())?.[0]?.[1];
1752
- return Number(resultData?.numUpdatedRows || 0);
1753
- }
1754
- /**
1755
- * @internal
1756
- *
1757
- * Used for applying first and last queries
1758
- *
1759
- * @returns A dream instance or null
1760
- */
1761
- async takeOne() {
1762
- if (this.joinLoadActivated) {
1763
- let query;
1764
- if (this.whereStatements.find(whereStatement => whereStatement[this.dreamClass.primaryKey] ||
1765
- whereStatement[this.namespacedPrimaryKey])) {
1766
- // the query already includes a primary key where statement
1767
- query = this;
1768
- }
1769
- else {
1770
- // otherwise find the primary key and apply it to the query
1771
- const primaryKeyValue = (await this.limit(1).pluck(this.namespacedPrimaryKey))[0];
1772
- if (primaryKeyValue === undefined)
1773
- return null;
1774
- query = this.where({ [this.namespacedPrimaryKey]: primaryKeyValue });
1775
- }
1776
- return (await query.executeJoinLoad())[0] || null;
1777
- }
1778
- const kyselyQuery = this.limit(1).buildSelect();
1779
- const results = await executeDatabaseQuery(kyselyQuery, 'executeTakeFirst');
1780
- if (results) {
1781
- const theFirst = sqlResultToDreamInstance(this.dreamClass, results);
1782
- if (theFirst)
1783
- await this.applyPreload(this.preloadStatements, this.preloadOnStatements, [theFirst]);
1784
- return theFirst;
1785
- }
1786
- else
1787
- return null;
1788
- }
1789
- /**
1790
- * @internal
1791
- *
1792
- * Used to hydrate dreams with the provided associations
1793
- */
1794
- hydrateAssociation(dreams, association, preloadedDreamsAndWhatTheyPointTo) {
1795
- switch (association.type) {
1796
- case 'HasMany':
1797
- dreams.forEach((dream) => {
1798
- dream[association.as] = [];
1799
- });
1800
- break;
1801
- default:
1802
- dreams.forEach((dream) => {
1803
- dream[associationToGetterSetterProp(association)] = null;
1804
- });
1805
- }
1806
- // dreams is a Rating
1807
- // Rating belongs to: rateables (Posts / Compositions)
1808
- // loadedAssociations is an array of Posts and Compositions
1809
- // if rating.rateable_id === loadedAssociation.primaryKeyvalue
1810
- // rating.rateable = loadedAssociation
1811
- preloadedDreamsAndWhatTheyPointTo.forEach(preloadedDreamAndWhatItPointsTo => {
1812
- dreams
1813
- .filter(dream => dream.primaryKeyValue === preloadedDreamAndWhatItPointsTo.pointsToPrimaryKey)
1814
- .forEach((dream) => {
1815
- if (association.type === 'HasMany') {
1816
- dream[association.as].push(preloadedDreamAndWhatItPointsTo.dream);
1817
- }
1818
- else {
1819
- // in a HasOne context, order clauses will be applied in advance,
1820
- // prior to hydration. Considering, we only want to set the first
1821
- // result and ignore other results, so we will use ||= to set.
1822
- dream[association.as] ||= preloadedDreamAndWhatItPointsTo.dream;
1823
- }
1824
- });
1825
- });
1826
- if (association.type === 'HasMany') {
1827
- dreams.forEach((dream) => Object.freeze(dream[association.as]));
1828
- }
1829
- }
1830
- /**
1831
- * @internal
1832
- *
1833
- * Used to bridge through associations
1834
- */
1835
- followThroughAssociation(dreamClass, association) {
1836
- const throughAssociation = association.through && dreamClass['getAssociationMetadata'](association.through);
1837
- if (!throughAssociation)
1838
- throw new MissingThroughAssociation({
1839
- dreamClass,
1840
- association,
1841
- });
1842
- const throughClass = throughAssociation.modelCB();
1843
- if (Array.isArray(throughClass))
1844
- throw new CannotAssociateThroughPolymorphic({
1845
- dreamClass,
1846
- association,
1847
- });
1848
- const newAssociation = getSourceAssociation(throughClass, association.source);
1849
- if (!newAssociation)
1850
- throw new MissingThroughAssociationSource({
1851
- dreamClass,
1852
- throughClass,
1853
- association,
1854
- });
1855
- return { throughAssociation, throughClass, newAssociation };
1856
- }
1857
- /**
1858
- * @internal
1859
- *
1860
- * Polymorphic BelongsTo. Since polymorphic associations may point to multiple tables,
1861
- * preload by loading each target class separately.
1862
- *
1863
- * Used to preload polymorphic belongs to associations
1864
- */
1865
- async preloadPolymorphicBelongsTo(association, dreams) {
1866
- if (!association.polymorphic)
1867
- throw new Error(`Association ${association.as} points to an array of models but is not designated polymorphic`);
1868
- if (association.type !== 'BelongsTo')
1869
- throw new Error(`Polymorphic association ${association.as} points to an array of models but is ${association.type}. Only BelongsTo associations may point to an array of models.`);
1870
- const associatedDreams = [];
1871
- for (const associatedModel of association.modelCB()) {
1872
- await this.preloadPolymorphicAssociationModel(dreams, association, associatedModel, associatedDreams);
1873
- }
1874
- return associatedDreams;
1875
- }
1876
- async preloadPolymorphicAssociationModel(dreams, association, associatedDreamClass, associatedDreams) {
1877
- const relevantAssociatedModels = dreams.filter((dream) => {
1878
- const field = association.foreignKeyTypeField();
1879
- return dream[field] === associatedDreamClass['stiBaseClassOrOwnClassName'] || dream[field] === null;
1880
- });
1881
- if (relevantAssociatedModels.length) {
1882
- dreams.forEach((dream) => {
1883
- dream[associationToGetterSetterProp(association)] = null;
1884
- });
1885
- // Load all models of type associated that are associated with any of the already loaded Dream models
1886
- const loadedAssociations = await this.dreamClassQueryWithScopeBypasses(associatedDreamClass, {
1887
- // The association may remove specific default scopes that would otherwise preclude
1888
- // certain instances of the associated class from being found.
1889
- defaultScopesToBypassExceptOnAssociations: association.withoutDefaultScopes,
1890
- })
1891
- .where({
1892
- [associatedDreamClass.primaryKey]: relevantAssociatedModels.map((dream) => dream[association.foreignKey()]),
1893
- })
1894
- .all();
1895
- loadedAssociations.forEach((loadedAssociation) => associatedDreams.push(loadedAssociation));
1896
- //////////////////////////////////////////////////////////////////////////////////////////////
1897
- // Associate each loaded association with each dream based on primary key and foreign key type
1898
- //////////////////////////////////////////////////////////////////////////////////////////////
1899
- for (const loadedAssociation of loadedAssociations) {
1900
- dreams
1901
- .filter((dream) => {
1902
- return (dream[association.foreignKeyTypeField()] === loadedAssociation['stiBaseClassOrOwnClassName'] &&
1903
- dream[association.foreignKey()] === association.primaryKeyValue(loadedAssociation));
1904
- })
1905
- .forEach((dream) => {
1906
- dream[association.as] = loadedAssociation;
1907
- });
1908
- }
1909
- ///////////////////////////////////////////////////////////////////////////////////////////////////
1910
- // end: Associate each loaded association with each dream based on primary key and foreign key type
1911
- ///////////////////////////////////////////////////////////////////////////////////////////////////
1912
- }
1913
- }
1914
- /**
1915
- * @internal
1916
- *
1917
- * Applies a preload statement
1918
- */
1919
- async applyOnePreload(associationName, dreams, onStatement = {}) {
1920
- if (!Array.isArray(dreams))
1921
- dreams = [dreams];
1922
- const dream = dreams.find(dream => dream['getAssociationMetadata'](associationName));
1923
- if (!dream)
1924
- return;
1925
- const { name, alias } = extractAssociationMetadataFromAssociationName(associationName);
1926
- const association = dream['getAssociationMetadata'](name);
1927
- if (association === undefined)
1928
- throw new UnexpectedUndefined();
1929
- const dreamClass = dream.constructor;
1930
- const dreamClassToHydrate = association.modelCB();
1931
- if ((association.polymorphic && association.type === 'BelongsTo') || Array.isArray(dreamClassToHydrate))
1932
- return this.preloadPolymorphicBelongsTo(association, dreams);
1933
- const dreamClassToHydrateColumns = [...dreamClassToHydrate.columns()];
1934
- const throughColumnsToHydrate = [];
1935
- const columnsToPluck = dreamClassToHydrateColumns.map(column => this.namespaceColumn(column.toString(), alias));
1936
- const asHasAssociation = association;
1937
- if (asHasAssociation.through && asHasAssociation.preloadThroughColumns) {
1938
- if (isObject(asHasAssociation.preloadThroughColumns)) {
1939
- const preloadMap = asHasAssociation.preloadThroughColumns;
1940
- Object.keys(preloadMap).forEach(preloadThroughColumn => {
1941
- throughColumnsToHydrate.push(preloadMap[preloadThroughColumn]);
1942
- columnsToPluck.push(this.namespaceColumn(preloadThroughColumn, asHasAssociation.through));
1943
- });
1944
- }
1945
- else {
1946
- const preloadArray = asHasAssociation.preloadThroughColumns;
1947
- preloadArray.forEach(preloadThroughColumn => {
1948
- throughColumnsToHydrate.push(preloadThroughColumn);
1949
- columnsToPluck.push(this.namespaceColumn(preloadThroughColumn, asHasAssociation.through));
1950
- });
1951
- }
1952
- }
1953
- columnsToPluck.push(this.namespaceColumn(dreamClass.primaryKey, dreamClass.table));
1954
- const baseClass = dreamClass['stiBaseClassOrOwnClass']['getAssociationMetadata'](associationName)
1955
- ? dreamClass['stiBaseClassOrOwnClass']
1956
- : dreamClass;
1957
- const associationDataScope = this.dreamClassQueryWithScopeBypasses(baseClass, {
1958
- // In order to stay DRY, preloading leverages the association logic built into
1959
- // `joins` (by using `pluck`, which calls `joins`). However, baseClass may have
1960
- // default scopes that would preclude finding that instance. We remove all
1961
- // default scopes on baseClass, but not subsequent associations, so that the
1962
- // single query will be able to find each row corresponding to a Dream in `dreams`,
1963
- // regardless of default scopes on that Dream's class.
1964
- bypassAllDefaultScopesExceptOnAssociations: true,
1965
- }).where({
1966
- [dreamClass.primaryKey]: dreams.map(obj => obj.primaryKeyValue),
1967
- });
1968
- const hydrationData = await associationDataScope
1969
- ._connection(this.connectionOverride)
1970
- .innerJoin(associationName, (onStatement || {}))
1971
- .pluck(...columnsToPluck);
1972
- const preloadedDreamsAndWhatTheyPointTo = hydrationData.map(pluckedData => {
1973
- const attributes = {};
1974
- dreamClassToHydrateColumns.forEach((columnName, index) => (attributes[protectAgainstPollutingAssignment(columnName)] = pluckedData[index]));
1975
- const hydratedDream = sqlResultToDreamInstance(dreamClassToHydrate, attributes);
1976
- throughColumnsToHydrate.forEach((throughAssociationColumn, index) => (hydratedDream.preloadedThroughColumns[throughAssociationColumn] =
1977
- pluckedData[dreamClassToHydrateColumns.length + index]));
1978
- return {
1979
- dream: hydratedDream,
1980
- pointsToPrimaryKey: pluckedData.at(-1),
1981
- };
1982
- });
1983
- this.hydrateAssociation(dreams, association, preloadedDreamsAndWhatTheyPointTo);
1984
- return preloadedDreamsAndWhatTheyPointTo.map(obj => obj.dream);
1985
- }
1986
- /**
1987
- * @internal
1988
- *
1989
- * Used by loadBuider
1990
- */
1991
- async hydratePreload(dream) {
1992
- await this.applyPreload(this.preloadStatements, this.preloadOnStatements, dream);
1993
- }
1994
- /**
1995
- * @internal
1996
- *
1997
- * Applies a preload statement
1998
- */
1999
- async applyPreload(preloadStatement, preloadOnStatements, dream) {
2000
- const keys = Object.keys(preloadStatement);
2001
- for (const key of keys) {
2002
- const nestedDreams = await this.applyOnePreload(key, dream, this.applyablePreloadOnStatements(preloadOnStatements[key]));
2003
- if (nestedDreams) {
2004
- await this.applyPreload(preloadStatement[key], preloadOnStatements[key], nestedDreams);
2005
- }
2006
- }
2007
- }
2008
- /**
2009
- * @internal
2010
- *
2011
- * retrieves on statements that can be applied to a preload
2012
- */
2013
- applyablePreloadOnStatements(preloadOnStatements) {
2014
- if (preloadOnStatements === undefined)
2015
- return undefined;
2016
- return Object.keys(preloadOnStatements).reduce((agg, key) => {
2017
- const value = preloadOnStatements[key];
2018
- if (value === undefined)
2019
- throw new UnexpectedUndefined();
2020
- // filter out plain objects, but not ops and not and/andNot/andAny statements
2021
- // because plain objects are just the next level of nested preload
2022
- if (key === 'and' ||
2023
- key === 'andNot' ||
2024
- key === 'andAny' ||
2025
- value === null ||
2026
- value.constructor !== Object) {
2027
- agg[key] = value;
2028
- }
2029
- return agg;
2030
- }, {});
2031
- }
2032
- conditionallyApplyDefaultScopes() {
2033
- if (this.bypassAllDefaultScopes || this.bypassAllDefaultScopesExceptOnAssociations)
2034
- return this;
2035
- const thisScopes = this.dreamClass['scopes'].default;
2036
- let query = this;
2037
- for (const scope of thisScopes) {
2038
- if (!shouldBypassDefaultScope(scope.method, {
2039
- defaultScopesToBypass: [
2040
- ...this.defaultScopesToBypass,
2041
- ...this.defaultScopesToBypassExceptOnAssociations,
2042
- ],
2043
- })) {
2044
- query = this.dreamClass[scope.method](query);
2045
- }
2046
- }
2047
- return query;
2048
- }
2049
- /**
2050
- * Each association in the chain is pushed onto `throughAssociations`
2051
- * and `applyOneJoin` is recursively called. The trick is that the
2052
- * through associations don't get written into the SQL; they
2053
- * locate the next association we need to build into the SQL,
2054
- * which is only run by the association that started the `through`
2055
- * chain. The final association at the end of the `through` chain _is_
2056
- * written into the SQL as a full association, but the modifications from
2057
- * the `through` association are only added when the recursion returns
2058
- * back to the association that kicked off the through associations.
2059
- */
2060
- joinsBridgeThroughAssociations({ query, dreamClass, association, previousAssociationTableOrAlias, throughAssociations, joinType, }) {
2061
- if (association.type === 'BelongsTo' || !association.through) {
2062
- return {
2063
- query,
2064
- dreamClass,
2065
- association,
2066
- previousAssociationTableOrAlias,
2067
- };
2068
- }
2069
- else {
2070
- throughAssociations.push(association);
2071
- // We have entered joinsBridgeThroughAssociations with the
2072
- // CompositionAssetAudits HasOne User association, which
2073
- // is through compositionAsset
2074
- // We now apply the compositionAsset association (a BelongsTo)
2075
- // to the query
2076
- const { query: queryWithThroughAssociationApplied } = this.applyOneJoin({
2077
- query,
2078
- dreamClass,
2079
- previousAssociationTableOrAlias,
2080
- currentAssociationTableOrAlias: association.through,
2081
- throughAssociations,
2082
- joinType,
2083
- });
2084
- // The through association has both a `through` and a `source`. The `source`
2085
- // is the association on the model that has now been joined. In our example,
2086
- // the `source` is the `user` association on the CompositionAsset model
2087
- const { newAssociation, throughAssociation, throughClass } = this.followThroughAssociation(dreamClass, association);
2088
- if (newAssociation.through) {
2089
- // This new association is itself a through association, so we recursively
2090
- // call joinsBridgeThroughAssociations
2091
- return this.joinsBridgeThroughAssociations({
2092
- query: queryWithThroughAssociationApplied,
2093
- dreamClass: throughClass,
2094
- association: newAssociation,
2095
- previousAssociationTableOrAlias: throughAssociation.as,
2096
- throughAssociations,
2097
- joinType,
2098
- });
2099
- }
2100
- else {
2101
- // This new association is not a through association, so
2102
- // this is the target association we were looking for
2103
- return {
2104
- query: queryWithThroughAssociationApplied,
2105
- dreamClass: association.modelCB(),
2106
- association: newAssociation,
2107
- throughClass,
2108
- previousAssociationTableOrAlias: association.through,
2109
- };
2110
- }
2111
- }
2112
- }
2113
- applyOneJoin({ query, dreamClass, previousAssociationTableOrAlias, currentAssociationTableOrAlias, joinAndStatements = {}, throughAssociations = [], joinType, }) {
2114
- const { name, alias } = extractAssociationMetadataFromAssociationName(currentAssociationTableOrAlias);
2115
- const joinAndStatement = joinAndStatements[currentAssociationTableOrAlias];
2116
- previousAssociationTableOrAlias = extractAssociationMetadataFromAssociationName(previousAssociationTableOrAlias).alias;
2117
- currentAssociationTableOrAlias = alias;
2118
- let association = dreamClass['getAssociationMetadata'](name);
2119
- if (!association) {
2120
- throw new JoinAttemptedOnMissingAssociation({
2121
- dreamClass,
2122
- associationName: currentAssociationTableOrAlias,
2123
- });
2124
- }
2125
- const results = this.joinsBridgeThroughAssociations({
2126
- query,
2127
- dreamClass,
2128
- association,
2129
- previousAssociationTableOrAlias,
2130
- throughAssociations,
2131
- joinType,
2132
- });
2133
- query = results.query;
2134
- dreamClass = results.dreamClass;
2135
- association = results.association;
2136
- const timeToApplyThroughAssociations = throughAssociations.length && throughAssociations[0]?.source === association.as;
2137
- const originalPreviousAssociationTableOrAlias = previousAssociationTableOrAlias;
2138
- previousAssociationTableOrAlias = results.previousAssociationTableOrAlias;
2139
- const throughClass = results.throughClass;
2140
- if (timeToApplyThroughAssociations) {
2141
- /**
2142
- * Each association in the chain is pushed onto `throughAssociations`
2143
- * and `applyOneJoin` is recursively called. The trick is that the
2144
- * through associations don't get written into the SQL; they
2145
- * locate the next association we need to build into the SQL,
2146
- * which is only run by the association that started the `through`
2147
- * chain (thus the
2148
- * `throughAssociations.length && throughAssociations[0].source === association.as`
2149
- * above). The final association at the end of the `through` chain _is_
2150
- * written into the SQL as a full association, but the modifications from
2151
- * the `through` association are only added when the recursion returns
2152
- * back to the association that kicked off the through associations.
2153
- */
2154
- throughAssociations.forEach((throughAssociation) => {
2155
- if (throughAssociation.type === 'HasMany') {
2156
- if (query?.distinctOn && throughAssociation.distinct) {
2157
- query = query.distinctOn(this.distinctColumnNameForAssociation({
2158
- association: throughAssociation,
2159
- tableNameOrAlias: currentAssociationTableOrAlias,
2160
- foreignKey: throughAssociation.primaryKey(),
2161
- }));
2162
- }
2163
- if (throughAssociation.order) {
2164
- query = this.applyOrderStatementForAssociation({
2165
- query,
2166
- association: throughAssociation,
2167
- tableNameOrAlias: currentAssociationTableOrAlias,
2168
- });
2169
- }
2170
- }
2171
- });
2172
- }
2173
- if (association.type === 'BelongsTo') {
2174
- if (Array.isArray(association.modelCB()))
2175
- throw new CannotJoinPolymorphicBelongsToError({
2176
- dreamClass,
2177
- association,
2178
- innerJoinStatements: this.innerJoinStatements,
2179
- leftJoinStatements: this.leftJoinStatements,
2180
- });
2181
- const to = association.modelCB().table;
2182
- const joinTableExpression = currentAssociationTableOrAlias === to
2183
- ? currentAssociationTableOrAlias
2184
- : `${to} as ${currentAssociationTableOrAlias}`;
2185
- query = query[(joinType === 'inner' ? 'innerJoin' : 'leftJoin')](joinTableExpression, (join) => {
2186
- join = join.onRef(this.namespaceColumn(association.foreignKey(), previousAssociationTableOrAlias), '=', this.namespaceColumn(association.primaryKey(), currentAssociationTableOrAlias));
2187
- if (timeToApplyThroughAssociations) {
2188
- throughAssociations.forEach((throughAssociation) => {
2189
- join = this.applyAssociationAndStatementsToJoinStatement({
2190
- join,
2191
- association: throughAssociation,
2192
- currentAssociationTableOrAlias,
2193
- previousAssociationTableOrAlias: originalPreviousAssociationTableOrAlias,
2194
- joinAndStatements,
2195
- });
2196
- });
2197
- }
2198
- join = this.conditionallyApplyDefaultScopesDependentOnAssociation({
2199
- join,
2200
- tableNameOrAlias: currentAssociationTableOrAlias,
2201
- association,
2202
- });
2203
- join = this.applyJoinAndStatement(join, joinAndStatement, currentAssociationTableOrAlias);
2204
- return join;
2205
- });
2206
- }
2207
- else {
2208
- const to = association.modelCB().table;
2209
- const joinTableExpression = currentAssociationTableOrAlias === to
2210
- ? currentAssociationTableOrAlias
2211
- : `${to} as ${currentAssociationTableOrAlias}`;
2212
- query = query[(joinType === 'inner' ? 'innerJoin' : 'leftJoin')](joinTableExpression, (join) => {
2213
- join = join.onRef(this.namespaceColumn(association.primaryKey(), previousAssociationTableOrAlias), '=', this.namespaceColumn(association.foreignKey(), currentAssociationTableOrAlias));
2214
- if (association.polymorphic) {
2215
- join = join.on((eb) => this.whereStatementToExpressionWrapper(eb, this.aliasWhereStatement({
2216
- [association.foreignKeyTypeField()]: throughClass
2217
- ? throughClass['stiBaseClassOrOwnClassName']
2218
- : dreamClass['stiBaseClassOrOwnClassName'],
2219
- }, currentAssociationTableOrAlias)));
2220
- }
2221
- if (timeToApplyThroughAssociations) {
2222
- throughAssociations.forEach((throughAssociation) => {
2223
- join = this.applyAssociationAndStatementsToJoinStatement({
2224
- join,
2225
- association: throughAssociation,
2226
- currentAssociationTableOrAlias,
2227
- previousAssociationTableOrAlias: originalPreviousAssociationTableOrAlias,
2228
- joinAndStatements,
2229
- });
2230
- });
2231
- }
2232
- join = this.applyAssociationAndStatementsToJoinStatement({
2233
- join,
2234
- association,
2235
- currentAssociationTableOrAlias,
2236
- previousAssociationTableOrAlias,
2237
- joinAndStatements,
2238
- });
2239
- join = this.conditionallyApplyDefaultScopesDependentOnAssociation({
2240
- join,
2241
- tableNameOrAlias: currentAssociationTableOrAlias,
2242
- association,
2243
- });
2244
- join = this.applyJoinAndStatement(join, joinAndStatement, currentAssociationTableOrAlias);
2245
- return join;
2246
- });
2247
- if (association.type === 'HasMany') {
2248
- if (association.order) {
2249
- query = this.applyOrderStatementForAssociation({
2250
- query,
2251
- tableNameOrAlias: currentAssociationTableOrAlias,
2252
- association,
2253
- });
2254
- }
2255
- if (query.distinctOn && association.distinct) {
2256
- query = query.distinctOn(this.distinctColumnNameForAssociation({
2257
- association,
2258
- tableNameOrAlias: currentAssociationTableOrAlias,
2259
- foreignKey: association.foreignKey(),
2260
- }));
2261
- }
2262
- }
2263
- }
2264
- return {
2265
- query,
2266
- association,
2267
- previousAssociationTableOrAlias,
2268
- currentAssociationTableOrAlias,
2269
- };
2270
- }
2271
- applyAssociationAndStatementsToJoinStatement({ join, currentAssociationTableOrAlias, previousAssociationTableOrAlias, association, joinAndStatements, }) {
2272
- if (association.and) {
2273
- this.throwUnlessAllRequiredWhereClausesProvided(association, currentAssociationTableOrAlias, joinAndStatements);
2274
- join = join.on((eb) => this.whereStatementToExpressionWrapper(eb, this.aliasWhereStatement(association.and, currentAssociationTableOrAlias), { disallowSimilarityOperator: false }));
2275
- }
2276
- if (association.andNot) {
2277
- join = join.on((eb) => this.whereStatementToExpressionWrapper(eb, this.aliasWhereStatement(association.andNot, currentAssociationTableOrAlias), { negate: true }));
2278
- }
2279
- if (association.andAny) {
2280
- join = join.on((eb) => eb.or(association.andAny.map(whereAnyStatement => this.whereStatementToExpressionWrapper(eb, this.aliasWhereStatement(whereAnyStatement, currentAssociationTableOrAlias), { disallowSimilarityOperator: false }))));
2281
- }
2282
- if (association.selfAnd) {
2283
- join = join.on((eb) => this.whereStatementToExpressionWrapper(eb, this.rawifiedSelfOnClause({
2284
- associationAlias: association.as,
2285
- selfAlias: previousAssociationTableOrAlias,
2286
- selfAndClause: association.selfAnd,
2287
- })));
2288
- }
2289
- if (association.selfAndNot) {
2290
- join = join.on((eb) => this.whereStatementToExpressionWrapper(eb, this.rawifiedSelfOnClause({
2291
- associationAlias: association.as,
2292
- selfAlias: previousAssociationTableOrAlias,
2293
- selfAndClause: association.selfAndNot,
2294
- }), { negate: true }));
2295
- }
2296
- return join;
2297
- }
2298
- conditionallyApplyDefaultScopesDependentOnAssociation({ join, tableNameOrAlias, association, }) {
2299
- let scopesQuery = new Query(this.dreamInstance);
2300
- const associationClass = association.modelCB();
2301
- const associationScopes = associationClass['scopes'].default;
2302
- for (const scope of associationScopes) {
2303
- if (!shouldBypassDefaultScope(scope.method, {
2304
- bypassAllDefaultScopes: this.bypassAllDefaultScopes,
2305
- defaultScopesToBypass: [...this.defaultScopesToBypass, ...(association.withoutDefaultScopes || [])],
2306
- })) {
2307
- const tempQuery = associationClass[scope.method](scopesQuery);
2308
- // The scope method on a Dream model should return a clone of the Query it receives
2309
- // (e.g. by returning `scope.where(...)`), but in case the function doesn't return,
2310
- // or returns the wrong thing, we check before overriding `scopesQuery` with what the
2311
- // method returned.
2312
- if (tempQuery && tempQuery.constructor === scopesQuery.constructor)
2313
- scopesQuery = tempQuery;
2314
- }
2315
- }
2316
- if (scopesQuery.whereStatements.length) {
2317
- join = join.on((eb) => eb.and(scopesQuery.whereStatements.flatMap(whereStatement => this.whereStatementToExpressionWrapper(eb, this.aliasWhereStatement(whereStatement, tableNameOrAlias), { disallowSimilarityOperator: false }))));
2318
- }
2319
- return join;
2320
- }
2321
- distinctColumnNameForAssociation({ association, tableNameOrAlias, foreignKey, }) {
2322
- if (!association.distinct)
2323
- return null;
2324
- if (association.distinct === true)
2325
- return this.namespaceColumn(foreignKey, tableNameOrAlias);
2326
- return this.namespaceColumn(association.distinct, tableNameOrAlias);
2327
- }
2328
- recursivelyJoin({ query, joinStatement, joinAndStatements, dreamClass, previousAssociationTableOrAlias, joinType, }) {
2329
- for (const currentAssociationTableOrAlias of Object.keys(joinStatement)) {
2330
- const results = this.applyOneJoin({
2331
- query,
2332
- dreamClass,
2333
- previousAssociationTableOrAlias,
2334
- currentAssociationTableOrAlias,
2335
- joinAndStatements,
2336
- joinType,
2337
- });
2338
- query = results.query;
2339
- const association = results.association;
2340
- query = this.recursivelyJoin({
2341
- query,
2342
- joinStatement: joinStatement[currentAssociationTableOrAlias],
2343
- joinAndStatements: joinAndStatements[currentAssociationTableOrAlias],
2344
- dreamClass: association.modelCB(),
2345
- previousAssociationTableOrAlias: currentAssociationTableOrAlias,
2346
- joinType,
2347
- });
2348
- }
2349
- return query;
2350
- }
2351
- throwUnlessAllRequiredWhereClausesProvided(association, namespace, joinAndStatements) {
2352
- const andClause = association.and;
2353
- const columnsRequiringAndStatements = Object.keys(andClause).reduce((agg, column) => {
2354
- if (andClause[column] === DreamConst.required)
2355
- agg.push(column);
2356
- return agg;
2357
- }, []);
2358
- const missingRequiredWhereStatements = columnsRequiringAndStatements.filter(column => joinAndStatements[namespace]?.and?.[column] === undefined);
2359
- if (missingRequiredWhereStatements.length)
2360
- throw new MissingRequiredAssociationAndClause(association, missingRequiredWhereStatements[0]);
2361
- }
2362
- applyOrderStatementForAssociation({ query, tableNameOrAlias, association, }) {
2363
- if (!query.orderBy)
2364
- return query;
2365
- let selectQuery = query;
2366
- const orderStatement = association.order;
2367
- if (typeof orderStatement === 'string') {
2368
- selectQuery = selectQuery.orderBy(this.namespaceColumn(orderStatement, tableNameOrAlias), 'asc');
2369
- }
2370
- else {
2371
- Object.keys(orderStatement).forEach(column => {
2372
- const direction = orderStatement[column];
2373
- selectQuery = selectQuery.orderBy(this.namespaceColumn(column, tableNameOrAlias), direction);
2374
- });
2375
- }
2376
- return selectQuery;
2377
- }
2378
- inArrayWithNull_or_notInArrayWithoutNull_ExpressionBuilder(eb, a, b, c) {
2379
- const isNullStatement = eb(a, 'is', null);
2380
- const compactedC = compact(c);
2381
- if (compactedC.length)
2382
- return eb.or([eb(a, b, compactedC), isNullStatement]);
2383
- // not in an empty array means match everything
2384
- if (b === 'not in')
2385
- return sql `TRUE`;
2386
- return isNullStatement;
2387
- }
2388
- inArrayWithoutNullExpressionBuilder(eb, a, b, c) {
2389
- const isNotNullStatement = eb(a, 'is not', null);
2390
- const compactedC = compact(c);
2391
- if (compactedC.length)
2392
- return eb.and([eb(a, 'in', compactedC), isNotNullStatement]);
2393
- // in an empty array means match nothing
2394
- return sql `FALSE`;
2395
- }
2396
- notInArrayWithNullExpressionBuilder(eb, a, b, c) {
2397
- const isNullStatement = eb(a, 'is not', null);
2398
- const compactedC = compact(c);
2399
- if (compactedC.length)
2400
- return eb.and([eb(a, 'not in', compactedC), isNullStatement]);
2401
- return isNullStatement;
2402
- }
2403
- whereStatementToExpressionWrapper(eb, whereStatement, { negate = false, disallowSimilarityOperator = true, } = {}) {
2404
- const clauses = compact(Object.keys(whereStatement)
2405
- .filter(key => whereStatement[key] !== DreamConst.required)
2406
- .map(attr => {
2407
- const val = whereStatement[attr];
2408
- if (val?.isOpsStatement &&
2409
- val.shouldBypassWhereStatement) {
2410
- if (disallowSimilarityOperator)
2411
- throw new Error('Similarity operator may not be used in whereAny');
2412
- // some ops statements are handled specifically in the select portion of the query,
2413
- // and should be ommited from the where clause directly
2414
- return;
2415
- }
2416
- const { a, b, c, a2, b2, c2 } = this.dreamWhereStatementToExpressionBuilderParts(attr, val);
2417
- // postgres is unable to handle WHERE IN statements with blank arrays, such as in
2418
- // "WHERE id IN ()", meaning that:
2419
- // 1. If we receive a blank array during an IN comparison,
2420
- // then we need to simply regurgitate a where statement which
2421
- // guarantees no records.
2422
- // 2. If we receive a blank array during a NOT IN comparison,
2423
- // then it is the same as the where statement not being present at all,
2424
- // resulting in a noop on our end
2425
- //
2426
- if (Array.isArray(c)) {
2427
- if ((b === 'in' && c.includes(null)) || (b === 'not in' && !c.includes(null))) {
2428
- return this.inArrayWithNull_or_notInArrayWithoutNull_ExpressionBuilder(eb, a, b, c);
2429
- }
2430
- else if (negate && b === 'in' && !c.includes(null)) {
2431
- return this.inArrayWithoutNullExpressionBuilder(eb, a, b, c);
2432
- }
2433
- else if (b === 'not in' && c.includes(null)) {
2434
- return this.notInArrayWithNullExpressionBuilder(eb, a, b, c);
2435
- }
2436
- const compactedC = compact(c);
2437
- if (b === 'in' && compactedC.length === 0) {
2438
- // in an empty array means match nothing
2439
- return sql `FALSE`;
2440
- }
2441
- else if (b === 'not in' && compactedC.length === 0) {
2442
- // not in an empty array means match everything
2443
- return sql `TRUE`;
2444
- }
2445
- else {
2446
- return eb(a, b, compactedC);
2447
- }
2448
- //
2449
- }
2450
- else if (b === '=' && c === null) {
2451
- return eb(a, 'is', null);
2452
- //
2453
- }
2454
- else if (b === '!=' && c === null) {
2455
- return eb(a, 'is not', null);
2456
- //
2457
- }
2458
- else if (b === '=' && negate) {
2459
- return eb.and([eb(a, '=', c), eb(a, 'is not', null)]);
2460
- //
2461
- }
2462
- else if (b === '!=' && c !== null) {
2463
- return eb.or([eb(a, '!=', c), eb(a, 'is', null)]);
2464
- //
2465
- }
2466
- else {
2467
- const expression = eb(a, b, c);
2468
- if (b2)
2469
- return expression.and(eb(a2, b2, c2));
2470
- return expression;
2471
- }
2472
- }));
2473
- return negate ? eb.not(eb.parens(eb.and(clauses))) : eb.and(clauses);
2474
- }
2475
- dreamWhereStatementToExpressionBuilderParts(attr, val) {
2476
- let a;
2477
- let b;
2478
- let c;
2479
- let a2 = null;
2480
- let b2 = null;
2481
- let c2 = null;
2482
- if (val instanceof Function && val !== DreamConst.passthrough) {
2483
- val = val();
2484
- }
2485
- else if (val === DreamConst.passthrough) {
2486
- const column = attr.split('.').at(-1);
2487
- if (this.passthroughOnStatement[column] === undefined)
2488
- throw new MissingRequiredPassthroughForAssociationAndClause(column);
2489
- val = this.passthroughOnStatement[column];
2490
- }
2491
- if (val === null) {
2492
- a = attr;
2493
- b = 'is';
2494
- c = val;
2495
- }
2496
- else if (['SelectQueryBuilder', 'SelectQueryBuilderImpl'].includes(val?.constructor?.name)) {
2497
- a = attr;
2498
- b = 'in';
2499
- c = val;
2500
- }
2501
- else if (Array.isArray(val)) {
2502
- a = attr;
2503
- b = 'in';
2504
- c = val.map(v => v instanceof DateTime || v instanceof CalendarDate
2505
- ? v.toSQL()
2506
- : typeof v === 'string'
2507
- ? normalizeUnicode(v)
2508
- : v);
2509
- }
2510
- else if (val instanceof CurriedOpsStatement) {
2511
- val = val.toOpsStatement(this.dreamClass, attr);
2512
- a = attr;
2513
- b = val.operator;
2514
- c = val.value;
2515
- }
2516
- else if (val instanceof OpsStatement) {
2517
- a = attr;
2518
- b = val.operator;
2519
- c = val.value;
2520
- }
2521
- else if (val instanceof Range) {
2522
- const rangeStart = val.begin;
2523
- const rangeEnd = val.end;
2524
- const excludeEnd = val.excludeEnd;
2525
- if (rangeStart && rangeEnd) {
2526
- a = attr;
2527
- b = '>=';
2528
- c = rangeStart;
2529
- a2 = attr;
2530
- b2 = excludeEnd ? '<' : '<=';
2531
- c2 = rangeEnd;
2532
- }
2533
- else if (rangeStart) {
2534
- a = attr;
2535
- b = '>=';
2536
- c = rangeStart;
2537
- }
2538
- else {
2539
- a = attr;
2540
- b = excludeEnd ? '<' : '<=';
2541
- c = rangeEnd;
2542
- }
2543
- }
2544
- else {
2545
- a = attr;
2546
- b = '=';
2547
- c = val;
2548
- }
2549
- if (c instanceof DateTime || c instanceof CalendarDate)
2550
- c = c.toSQL();
2551
- else if (typeof c === 'string')
2552
- c = normalizeUnicode(c);
2553
- if (c2 instanceof DateTime || c2 instanceof CalendarDate)
2554
- c2 = c2.toSQL();
2555
- else if (typeof c2 === 'string')
2556
- c2 = normalizeUnicode(c2);
2557
- if (a && c === undefined)
2558
- throw new CannotPassUndefinedAsAValueToAWhereClause(this.dreamClass, a);
2559
- if (a2 && c2 === undefined)
2560
- throw new CannotPassUndefinedAsAValueToAWhereClause(this.dreamClass, a2);
2561
- return { a, b, c, a2, b2, c2 };
2562
- }
2563
- applyJoinAndStatement(join, joinAndStatement, rootTableOrAssociationAlias) {
2564
- if (!joinAndStatement)
2565
- return join;
2566
- join = this._applyJoinAndStatements(join, joinAndStatement.and, rootTableOrAssociationAlias);
2567
- join = this._applyJoinAndStatements(join, joinAndStatement.andNot, rootTableOrAssociationAlias, {
2568
- negate: true,
2569
- });
2570
- join = this._applyJoinAndAnyStatements(join, joinAndStatement.andAny, rootTableOrAssociationAlias);
2571
- return join;
2572
- }
2573
- _applyJoinAndStatements(join, joinAndStatement, rootTableOrAssociationAlias, { negate = false, } = {}) {
2574
- if (!joinAndStatement)
2575
- return join;
2576
- return join.on((eb) => this.joinAndStatementToExpressionWrapper(joinAndStatement, rootTableOrAssociationAlias, eb, {
2577
- negate,
2578
- disallowSimilarityOperator: negate,
2579
- }));
2580
- }
2581
- _applyJoinAndAnyStatements(join, joinAndAnyStatement, rootTableOrAssociationAlias) {
2582
- if (!joinAndAnyStatement)
2583
- return join;
2584
- if (!joinAndAnyStatement.length)
2585
- return join;
2586
- return join.on((eb) => {
2587
- return eb.or(joinAndAnyStatement.map(joinAndStatement => this.joinAndStatementToExpressionWrapper(joinAndStatement, rootTableOrAssociationAlias, eb)));
2588
- });
2589
- }
2590
- joinAndStatementToExpressionWrapper(joinAndStatement, rootTableOrAssociationAlias, eb, { negate = false, disallowSimilarityOperator = true, } = {}) {
2591
- return this.whereStatementToExpressionWrapper(eb, Object.keys(joinAndStatement).reduce((agg, key) => {
2592
- agg[this.namespaceColumn(key.toString(), rootTableOrAssociationAlias)] = joinAndStatement[key];
2593
- return agg;
2594
- }, {}), {
2595
- negate,
2596
- disallowSimilarityOperator,
2597
- });
2598
- }
2599
- buildCommon(kyselyQuery) {
2600
- this.checkForQueryViolations();
2601
- const query = this.conditionallyApplyDefaultScopes();
2602
- if (!isEmpty(query.innerJoinStatements)) {
2603
- kyselyQuery = query.recursivelyJoin({
2604
- query: kyselyQuery,
2605
- joinStatement: query.innerJoinStatements,
2606
- joinAndStatements: query.innerJoinAndStatements,
2607
- dreamClass: query.dreamClass,
2608
- previousAssociationTableOrAlias: this.baseSqlAlias,
2609
- joinType: 'inner',
2610
- });
2611
- }
2612
- if (!isEmpty(query.leftJoinStatements)) {
2613
- kyselyQuery = query.recursivelyJoin({
2614
- query: kyselyQuery,
2615
- joinStatement: query.leftJoinStatements,
2616
- joinAndStatements: query.leftJoinAndStatements,
2617
- dreamClass: query.dreamClass,
2618
- previousAssociationTableOrAlias: this.baseSqlAlias,
2619
- joinType: 'left',
2620
- });
2621
- }
2622
- if (query.whereStatements.length || query.whereNotStatements.length || query.whereAnyStatements.length) {
2623
- kyselyQuery = kyselyQuery.where((eb) => eb.and([
2624
- ...this.aliasWhereStatements(query.whereStatements, query.baseSqlAlias).map(whereStatement => this.whereStatementToExpressionWrapper(eb, whereStatement, {
2625
- disallowSimilarityOperator: false,
2626
- })),
2627
- ...this.aliasWhereStatements(query.whereNotStatements, query.baseSqlAlias).map(whereNotStatement => this.whereStatementToExpressionWrapper(eb, whereNotStatement, { negate: true })),
2628
- ...query.whereAnyStatements.map(whereAnyStatements => eb.or(this.aliasWhereStatements(whereAnyStatements, query.baseSqlAlias).map(whereAnyStatement => this.whereStatementToExpressionWrapper(eb, whereAnyStatement)))),
2629
- ]));
2630
- }
2631
- return kyselyQuery;
2632
- }
2633
- checkForQueryViolations() {
2634
- const invalidWhereNotClauses = this.similarityStatementBuilder().whereNotStatementsWithSimilarityClauses();
2635
- if (invalidWhereNotClauses.length) {
2636
- const invalidWhereNotClause = invalidWhereNotClauses[0];
2637
- if (invalidWhereNotClause === undefined)
2638
- throw new UnexpectedUndefined();
2639
- const { tableName, columnName, opsStatement } = invalidWhereNotClause;
2640
- throw new CannotNegateSimilarityClause(tableName, columnName, opsStatement.value);
2641
- }
2642
- }
2643
- aliasWhereStatements(whereStatements, alias) {
2644
- return whereStatements.map(whereStatement => this.aliasWhereStatement(whereStatement, alias));
2645
- }
2646
- aliasWhereStatement(whereStatement, alias) {
2647
- return Object.keys(whereStatement).reduce((aliasedWhere, key) => {
2648
- aliasedWhere[this.namespaceColumn(key, alias)] = whereStatement[key];
2649
- return aliasedWhere;
2650
- }, {});
2651
- }
2652
- rawifiedSelfOnClause({ associationAlias, selfAlias, selfAndClause, }) {
2653
- const alphanumericUnderscoreRegexp = /[^a-zA-Z0-9_]/g;
2654
- selfAlias = selfAlias.replace(alphanumericUnderscoreRegexp, '');
2655
- return Object.keys(selfAndClause).reduce((acc, key) => {
2656
- const selfColumn = selfAndClause[key]?.replace(alphanumericUnderscoreRegexp, '');
2657
- if (!selfColumn)
2658
- return acc;
2659
- acc[this.namespaceColumn(key, associationAlias)] = sql.raw(`"${snakeify(selfAlias)}"."${snakeify(selfColumn)}"`);
2660
- return acc;
2661
- }, {});
2662
- }
2663
- buildDelete() {
2664
- const kyselyQuery = this.dbFor('delete').deleteFrom(this.baseSqlAlias);
2665
- const results = this.attachLimitAndOrderStatementsToNonSelectQuery(kyselyQuery);
2666
- return results.clone.buildCommon(results.kyselyQuery);
2667
- }
2668
- buildSelect({ bypassSelectAll = false, bypassOrder = false, columns, } = {}) {
2669
- let kyselyQuery;
2670
- if (this.baseSelectQuery) {
2671
- kyselyQuery = (this.connectionOverride
2672
- ? this.baseSelectQuery.connection(this.connectionOverride)
2673
- : this.baseSelectQuery).buildSelect({ bypassSelectAll: true });
2674
- }
2675
- else {
2676
- const from = this.baseSqlAlias === this.tableName ? this.tableName : `${this.tableName} as ${this.baseSqlAlias}`;
2677
- kyselyQuery = this.dbFor('select').selectFrom(from);
2678
- }
2679
- if (this.distinctColumn) {
2680
- kyselyQuery = kyselyQuery.distinctOn(this.distinctColumn);
2681
- }
2682
- kyselyQuery = this.buildCommon(kyselyQuery);
2683
- kyselyQuery = this.conditionallyAttachSimilarityColumnsToSelect(kyselyQuery, {
2684
- bypassOrder: bypassOrder || !!this.distinctColumn,
2685
- });
2686
- if (this.orderStatements.length && !bypassOrder) {
2687
- this.orderStatements.forEach(orderStatement => {
2688
- kyselyQuery = kyselyQuery.orderBy(this.namespaceColumn(orderStatement.column), orderByDirection(orderStatement.direction));
2689
- });
2690
- }
2691
- if (this.limitStatement)
2692
- kyselyQuery = kyselyQuery.limit(this.limitStatement);
2693
- if (this.offsetStatement)
2694
- kyselyQuery = kyselyQuery.offset(this.offsetStatement);
2695
- if (columns) {
2696
- kyselyQuery = kyselyQuery.select(this.columnsWithRequiredLoadColumns(columns).map(column => this.namespaceColumn(column)));
2697
- }
2698
- else if (!bypassSelectAll) {
2699
- kyselyQuery = kyselyQuery.selectAll(this.baseSqlAlias);
2700
- }
2701
- // even though we manually bypass explicit order statements above,
2702
- // associations can contain their own ordering systems. If we do not
2703
- // escape all orders, we can mistakenly allow an order clause to sneak in.
2704
- if (bypassOrder)
2705
- kyselyQuery = kyselyQuery.clearOrderBy();
2706
- return kyselyQuery;
2707
- }
2708
- columnsWithRequiredLoadColumns(columns) {
2709
- return uniq(compact([this.dreamClass.primaryKey, this.dreamClass['isSTIBase'] ? 'type' : null, ...columns]));
2710
- }
2711
- buildUpdate(attributes) {
2712
- let kyselyQuery = this.dbFor('update')
2713
- .updateTable(this.tableName)
2714
- .set(attributes);
2715
- kyselyQuery = this.conditionallyAttachSimilarityColumnsToUpdate(kyselyQuery);
2716
- const results = this.attachLimitAndOrderStatementsToNonSelectQuery(kyselyQuery);
2717
- return results.clone.buildCommon(results.kyselyQuery);
2718
- }
2719
- attachLimitAndOrderStatementsToNonSelectQuery(kyselyQuery) {
2720
- if (this.limitStatement || this.orderStatements.length) {
2721
- kyselyQuery = kyselyQuery.where((eb) => {
2722
- const subquery = this.nestedSelect(this.dreamInstance.primaryKey);
2723
- return eb(this.dreamInstance.primaryKey, 'in', subquery);
2724
- });
2725
- return {
2726
- kyselyQuery,
2727
- clone: this.clone({ where: null, whereNot: null, order: null, limit: null }),
2728
- };
2729
- }
2730
- return { kyselyQuery, clone: this };
2731
- }
2732
- get hasSimilarityClauses() {
2733
- return this.similarityStatementBuilder().hasSimilarityClauses;
2734
- }
2735
- similarityStatementBuilder() {
2736
- return new SimilarityBuilder(this.dreamInstance, {
2737
- where: [...this.whereStatements],
2738
- whereNot: [...this.whereNotStatements],
2739
- joinAndStatements: this.innerJoinAndStatements,
2740
- transaction: this.dreamTransaction,
2741
- connection: this.connectionOverride,
2742
- });
2743
- }
2744
- conditionallyAttachSimilarityColumnsToSelect(kyselyQuery, { bypassOrder = false } = {}) {
2745
- const similarityBuilder = this.similarityStatementBuilder();
2746
- if (similarityBuilder.hasSimilarityClauses) {
2747
- kyselyQuery = similarityBuilder.select(kyselyQuery, { bypassOrder });
2748
- }
2749
- return kyselyQuery;
2750
- }
2751
- conditionallyAttachSimilarityColumnsToUpdate(kyselyQuery) {
2752
- const similarityBuilder = this.similarityStatementBuilder();
2753
- if (similarityBuilder.hasSimilarityClauses) {
2754
- kyselyQuery = similarityBuilder.update(kyselyQuery);
2755
- }
2756
- return kyselyQuery;
1393
+ return await this.dbDriverInstance().update(attributes);
2757
1394
  }
2758
1395
  invertOrder() {
2759
1396
  let query = this.clone({ order: null });
@@ -2765,11 +1402,3 @@ export default class Query extends ConnectedToDB {
2765
1402
  return query;
2766
1403
  }
2767
1404
  }
2768
- function getSourceAssociation(dream, sourceName) {
2769
- if (!dream)
2770
- return;
2771
- if (!sourceName)
2772
- return;
2773
- return (dream['getAssociationMetadata'](sourceName) ||
2774
- dream['getAssociationMetadata'](pluralize.singular(sourceName)));
2775
- }