@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
@@ -0,0 +1,1768 @@
1
+ import { sql, } from 'kysely';
2
+ import pluralize from 'pluralize-esm';
3
+ import DreamCLI from '../../cli/index.js';
4
+ import _db from '../../db/index.js';
5
+ import associationToGetterSetterProp from '../../decorators/field/association/associationToGetterSetterProp.js';
6
+ import DreamApp from '../../dream-app/index.js';
7
+ import CannotAssociateThroughPolymorphic from '../../errors/associations/CannotAssociateThroughPolymorphic.js';
8
+ import CannotJoinPolymorphicBelongsToError from '../../errors/associations/CannotJoinPolymorphicBelongsToError.js';
9
+ import JoinAttemptedOnMissingAssociation from '../../errors/associations/JoinAttemptedOnMissingAssociation.js';
10
+ import MissingRequiredAssociationAndClause from '../../errors/associations/MissingRequiredAssociationAndClause.js';
11
+ import MissingRequiredPassthroughForAssociationAndClause from '../../errors/associations/MissingRequiredPassthroughForAssociationAndClause.js';
12
+ import MissingThroughAssociation from '../../errors/associations/MissingThroughAssociation.js';
13
+ import MissingThroughAssociationSource from '../../errors/associations/MissingThroughAssociationSource.js';
14
+ import CannotNegateSimilarityClause from '../../errors/CannotNegateSimilarityClause.js';
15
+ import CannotPassUndefinedAsAValueToAWhereClause from '../../errors/CannotPassUndefinedAsAValueToAWhereClause.js';
16
+ import UnexpectedUndefined from '../../errors/UnexpectedUndefined.js';
17
+ import CalendarDate from '../../helpers/CalendarDate.js';
18
+ import camelize from '../../helpers/camelize.js';
19
+ import generateMigration from '../../helpers/cli/generateMigration.js';
20
+ import compact from '../../helpers/compact.js';
21
+ import { DateTime } from '../../helpers/DateTime.js';
22
+ import loadPgClient from '../../helpers/db/loadPgClient.js';
23
+ import runMigration from '../../helpers/db/runMigration.js';
24
+ import EnvInternal from '../../helpers/EnvInternal.js';
25
+ import isEmpty from '../../helpers/isEmpty.js';
26
+ import isObject from '../../helpers/isObject.js';
27
+ import namespaceColumn from '../../helpers/namespaceColumn.js';
28
+ import normalizeUnicode from '../../helpers/normalizeUnicode.js';
29
+ import objectPathsToArrays from '../../helpers/objectPathsToArrays.js';
30
+ import protectAgainstPollutingAssignment from '../../helpers/protectAgainstPollutingAssignment.js';
31
+ import { Range } from '../../helpers/range.js';
32
+ import snakeify from '../../helpers/snakeify.js';
33
+ import sqlAttributes from '../../helpers/sqlAttributes.js';
34
+ import uniq from '../../helpers/uniq.js';
35
+ import CurriedOpsStatement from '../../ops/curried-ops-statement.js';
36
+ import OpsStatement from '../../ops/ops-statement.js';
37
+ import { DreamConst } from '../constants.js';
38
+ import executeDatabaseQuery from '../internal/executeDatabaseQuery.js';
39
+ import extractAssociationMetadataFromAssociationName from '../internal/extractAssociationMetadataFromAssociationName.js';
40
+ import orderByDirection from '../internal/orderByDirection.js';
41
+ import shouldBypassDefaultScope from '../internal/shouldBypassDefaultScope.js';
42
+ import SimilarityBuilder from '../internal/similarity/SimilarityBuilder.js';
43
+ import sqlResultToDreamInstance from '../internal/sqlResultToDreamInstance.js';
44
+ import Query from '../Query.js';
45
+ import QueryDriverBase from './Base.js';
46
+ import writeSyncFile from '../../bin/helpers/sync.js';
47
+ import SchemaBuilder from '../../helpers/cli/SchemaBuilder.js';
48
+ import createDb from '../../helpers/db/createDb.js';
49
+ import _dropDb from '../../helpers/db/dropDb.js';
50
+ export default class KyselyQueryDriver extends QueryDriverBase {
51
+ // ATTENTION FRED
52
+ // stop trying to make this async. You never learn...
53
+ //
54
+ // if you are attempting to leverage a kysely-based connection using a different driver,
55
+ // you would want to extend this KyselyQueryDriver class, and then change this method
56
+ // to return a custom kysely instance, tailored to the specific database connection
57
+ // you are attempting to support. By default, dream is postgres-only, so our internal _db
58
+ // function will return a kysely instance, tightly coupled to a postgres-specific connection.
59
+ dbFor(sqlCommandType) {
60
+ if (this.dreamTransaction?.kyselyTransaction)
61
+ return this.dreamTransaction?.kyselyTransaction;
62
+ return _db(this.dbConnectionType(sqlCommandType));
63
+ }
64
+ /**
65
+ * migrate the database. Must respond to the NODE_ENV value.
66
+ */
67
+ static async migrate() {
68
+ const dreamApp = DreamApp.getOrFail();
69
+ const primaryDbConf = dreamApp.dbConnectionConfig('primary');
70
+ DreamCLI.logger.logStartProgress(`migrating ${primaryDbConf.name}...`);
71
+ await runMigration({ mode: 'migrate' });
72
+ DreamCLI.logger.logEndProgress();
73
+ await this.duplicateDatabase();
74
+ }
75
+ /**
76
+ * rollback the database. Must respond to the NODE_ENV value.
77
+ */
78
+ static async rollback(opts) {
79
+ const dreamApp = DreamApp.getOrFail();
80
+ const primaryDbConf = dreamApp.dbConnectionConfig('primary');
81
+ DreamCLI.logger.logStartProgress(`rolling back ${primaryDbConf.name}...`);
82
+ let step = opts.steps;
83
+ while (step > 0) {
84
+ await runMigration({ mode: 'rollback' });
85
+ step -= 1;
86
+ }
87
+ DreamCLI.logger.logEndProgress();
88
+ await this.duplicateDatabase();
89
+ }
90
+ static async generateMigration(migrationName, columnsWithTypes) {
91
+ await generateMigration({ migrationName, columnsWithTypes });
92
+ }
93
+ static async sync(onSync) {
94
+ DreamCLI.logger.logStartProgress('writing db schema...');
95
+ await writeSyncFile();
96
+ DreamCLI.logger.logEndProgress();
97
+ DreamCLI.logger.logStartProgress('building dream schema...');
98
+ await new SchemaBuilder().build();
99
+ DreamCLI.logger.logEndProgress();
100
+ // intentionally leaving logs off here, since it allows other
101
+ // onSync handlers to determine their own independent logging approach
102
+ await onSync();
103
+ }
104
+ /**
105
+ * create the database. Must respond to the NODE_ENV value.
106
+ */
107
+ static async dbCreate() {
108
+ const dreamApp = DreamApp.getOrFail();
109
+ const primaryDbConf = dreamApp.dbConnectionConfig('primary');
110
+ DreamCLI.logger.logStartProgress(`creating ${primaryDbConf.name}...`);
111
+ await createDb('primary');
112
+ DreamCLI.logger.logEndProgress();
113
+ // TODO: add support for creating replicas. Began doing it below, but it is very tricky,
114
+ // and we don't need it at the moment, so kicking off for future development when we have more time
115
+ // to flesh this out.
116
+ // if (connectionRetriever.hasReplicaConfig()) {
117
+ // const replicaDbConf = connectionRetriever.getConnectionConf('replica')
118
+ // console.log(`creating ${process.env[replicaDbConf.name]}`)
119
+ // await createDb('replica')
120
+ // }
121
+ }
122
+ /**
123
+ * delete the database. Must respond to the NODE_ENV value.
124
+ */
125
+ static async dbDrop() {
126
+ const dreamApp = DreamApp.getOrFail();
127
+ const primaryDbConf = dreamApp.dbConnectionConfig('primary');
128
+ DreamCLI.logger.logStartProgress(`dropping ${primaryDbConf.name}...`);
129
+ await _dropDb('primary');
130
+ DreamCLI.logger.logEndProgress();
131
+ // TODO: add support for dropping replicas. Began doing it below, but it is very tricky,
132
+ // and we don't need it at the moment, so kicking off for future development when we have more time
133
+ // to flesh this out.
134
+ // if (connectionRetriever.hasReplicaConfig()) {
135
+ // const replicaDbConf = connectionRetriever.getConnectionConf('replica')
136
+ // console.log(`dropping ${process.env[replicaDbConf.name]}`)
137
+ // await _dropDb('replica')
138
+ // }
139
+ }
140
+ /**
141
+ * Converts the given dream class into a Kysely query, enabling
142
+ * you to build custom queries using the Kysely API
143
+ *
144
+ * ```ts
145
+ * await User.query().toKysely('select').where('email', '=', 'how@yadoin').execute()
146
+ * ```
147
+ *
148
+ * @param type - the type of Kysely query builder instance you would like to obtain
149
+ * @returns A Kysely query. Depending on the type passed, it will return either a SelectQueryBuilder, DeleteQueryBuilder, or an UpdateQueryBuilder
150
+ */
151
+ toKysely(type) {
152
+ switch (type) {
153
+ case 'select':
154
+ return this.buildSelect();
155
+ case 'delete':
156
+ return this.buildDelete();
157
+ case 'update':
158
+ return this.buildUpdate({});
159
+ // TODO: in the future, we should support insert type, but don't yet, since inserts are done outside
160
+ // the query class for some reason.
161
+ default: {
162
+ // protection so that if a new QueryType is ever added, this will throw a type error at build time
163
+ const _never = type;
164
+ throw new Error(`Unhandled QueryType: ${_never}`);
165
+ }
166
+ }
167
+ }
168
+ /**
169
+ * @internal
170
+ *
171
+ * This method is used internally by a Query driver to
172
+ * take the result of a single row in a database, and
173
+ * turn that row into the provided dream instance.
174
+ *
175
+ * If needed, the return type can be overriden to
176
+ * explicitly define the resulting dream instance,
177
+ * in cases where a proper type for the dream class
178
+ * cannot be inferred, i.e.
179
+ *
180
+ * ```ts
181
+ * this.dbResultToDreamInstance<typeof Dream, DreamInstance>(result, this.dreamClass)
182
+ * ```
183
+ */
184
+ dbResultToDreamInstance(result, dreamClass) {
185
+ return sqlResultToDreamInstance(dreamClass, result);
186
+ }
187
+ /**
188
+ * @internal
189
+ *
190
+ * Used for applying first and last queries
191
+ *
192
+ * @returns A dream instance or null
193
+ */
194
+ async takeOne() {
195
+ if (this.query['joinLoadActivated']) {
196
+ let query;
197
+ if (this.query['whereStatements'].find(whereStatement => whereStatement[this.dreamClass.primaryKey] ||
198
+ whereStatement[this.query['namespacedPrimaryKey']])) {
199
+ // the query already includes a primary key where statement
200
+ query = this.query;
201
+ }
202
+ else {
203
+ // otherwise find the primary key and apply it to the query
204
+ const primaryKeyValue = (await this.query.limit(1).pluck(this.query['namespacedPrimaryKey']))[0];
205
+ if (primaryKeyValue === undefined)
206
+ return null;
207
+ query = this.query.where({ [this.query['namespacedPrimaryKey']]: primaryKeyValue });
208
+ }
209
+ const driverClass = this.constructor;
210
+ return (await new driverClass(query)['executeJoinLoad']())[0] || null;
211
+ }
212
+ const kyselyQuery = new KyselyQueryDriver(this.query.limit(1)).buildSelect();
213
+ const results = await executeDatabaseQuery(kyselyQuery, 'executeTakeFirst');
214
+ if (results) {
215
+ const theFirst = this.dbResultToDreamInstance(results, this.dreamClass);
216
+ if (theFirst)
217
+ await this.applyPreload(this.query['preloadStatements'], this.query['preloadOnStatements'], [theFirst]);
218
+ return theFirst;
219
+ }
220
+ else
221
+ return null;
222
+ }
223
+ /**
224
+ * Retrieves an array containing all records matching the Query.
225
+ * Be careful using this, since it will attempt to pull every
226
+ * record into memory at once. When querying might return a large
227
+ * number of records, consider using `.findEach`, which will pull
228
+ * the records in batches.
229
+ *
230
+ * ```ts
231
+ * await User.query().all()
232
+ * ```
233
+ *
234
+ * @returns an array of dreams
235
+ */
236
+ async takeAll(options = {}) {
237
+ if (this.query['joinLoadActivated'])
238
+ return await this.executeJoinLoad(options);
239
+ const kyselyQuery = this.buildSelect(options);
240
+ const results = await executeDatabaseQuery(kyselyQuery, 'execute');
241
+ const theAll = results.map(r => this.dbResultToDreamInstance(r, this.dreamClass));
242
+ await this.applyPreload(this.query['preloadStatements'], this.query['preloadOnStatements'], theAll);
243
+ return theAll;
244
+ }
245
+ /**
246
+ * Retrieves the max value of the specified column
247
+ * for this Query
248
+ *
249
+ * ```ts
250
+ * await User.query().max('id')
251
+ * // 99
252
+ * ```
253
+ *
254
+ * @param columnName - a column name on the model
255
+ * @returns the max value of the specified column for this Query
256
+ *
257
+ */
258
+ async max(columnName) {
259
+ // eslint-disable-next-line @typescript-eslint/unbound-method
260
+ const { max } = this.dbFor('select').fn;
261
+ let kyselyQuery = new KyselyQueryDriver(this.query).buildSelect({
262
+ bypassSelectAll: true,
263
+ bypassOrder: true,
264
+ });
265
+ kyselyQuery = kyselyQuery.select(max(columnName));
266
+ const data = await executeDatabaseQuery(kyselyQuery, 'executeTakeFirstOrThrow');
267
+ return data.max;
268
+ }
269
+ /**
270
+ * Retrieves the min value of the specified column
271
+ * for this Query
272
+ *
273
+ * ```ts
274
+ * await User.query().min('id')
275
+ * // 1
276
+ * ```
277
+ *
278
+ * @param columnName - a column name on the model
279
+ * @returns the min value of the specified column for this Query
280
+ */
281
+ async min(columnName) {
282
+ // eslint-disable-next-line @typescript-eslint/unbound-method
283
+ const { min } = this.dbFor('select').fn;
284
+ let kyselyQuery = new KyselyQueryDriver(this.query).buildSelect({
285
+ bypassSelectAll: true,
286
+ bypassOrder: true,
287
+ });
288
+ kyselyQuery = kyselyQuery.select(min(columnName));
289
+ const data = await executeDatabaseQuery(kyselyQuery, 'executeTakeFirstOrThrow');
290
+ return data.min;
291
+ }
292
+ /**
293
+ * Retrieves the number of records in the database
294
+ *
295
+ * ```ts
296
+ * await User.query().count()
297
+ * ```
298
+ *
299
+ * @returns The number of records in the database
300
+ */
301
+ async count() {
302
+ // eslint-disable-next-line @typescript-eslint/unbound-method
303
+ const { count } = this.dbFor('select').fn;
304
+ const distinctColumn = this.query['distinctColumn'];
305
+ const query = this.query.clone({ distinctColumn: null });
306
+ let kyselyQuery = new KyselyQueryDriver(query).buildSelect({ bypassSelectAll: true, bypassOrder: true });
307
+ const countClause = distinctColumn
308
+ ? count(sql `DISTINCT ${distinctColumn}`)
309
+ : count(query['namespaceColumn'](query.dreamInstance.primaryKey));
310
+ kyselyQuery = kyselyQuery.select(countClause.as('tablecount'));
311
+ const data = await executeDatabaseQuery(kyselyQuery, 'executeTakeFirstOrThrow');
312
+ return parseInt(data.tablecount.toString());
313
+ }
314
+ /**
315
+ * @internal
316
+ *
317
+ * Runs the query and extracts plucked values
318
+ *
319
+ * @returns An array of plucked values
320
+ */
321
+ async pluck(...fields) {
322
+ let kyselyQuery = new KyselyQueryDriver(this.query['removeAllDefaultScopesExceptOnAssociations']()).buildSelect({
323
+ bypassSelectAll: true,
324
+ });
325
+ const aliases = [];
326
+ fields.forEach((field) => {
327
+ // field will already be namespaced in a join situation, but when the field to pluck is on the
328
+ // base model, it will be underscored (to match the table name), but when the selected column
329
+ // comes back from Kysely camelCased
330
+ aliases.push(field.includes('_') ? camelize(field) : field);
331
+ // namespace the selection so that when plucking the same column name from
332
+ // multpile tables, they don't get saved as the same name (e.g. select results with two `id` columns,
333
+ // which the pg package then returns in an object with a single `id` key)
334
+ kyselyQuery = kyselyQuery.select(`${this.namespaceColumn(field)} as ${field}`);
335
+ });
336
+ return (await executeDatabaseQuery(kyselyQuery, 'execute')).map(singleResult => aliases.map(alias => singleResult[alias]));
337
+ }
338
+ /**
339
+ * Returns a new Kysely SelectQueryBuilder instance to be used
340
+ * in a sub Query
341
+ *
342
+ * ```ts
343
+ * const records = await User.where({
344
+ * id: Post.query().nestedSelect('userId'),
345
+ * }).all()
346
+ * // [User{id: 1}, ...]
347
+ * ```
348
+ *
349
+ * @param selection - the column to use for your nested Query
350
+ * @returns A Kysely SelectQueryBuilder instance
351
+ */
352
+ nestedSelect(selection) {
353
+ const query = this.buildSelect({
354
+ bypassSelectAll: true,
355
+ bypassOrder: true,
356
+ });
357
+ return query.select(this.namespaceColumn(selection));
358
+ }
359
+ /**
360
+ * executes provided query instance as a deletion query.
361
+ * @returns the number of deleted rows
362
+ */
363
+ async delete() {
364
+ const deletionResult = await executeDatabaseQuery(this.buildDelete(), 'executeTakeFirst');
365
+ return Number(deletionResult?.numDeletedRows || 0);
366
+ }
367
+ /**
368
+ * executes provided query instance as an update query.
369
+ * @returns the number of updated rows
370
+ */
371
+ async update(attributes) {
372
+ const kyselyQuery = this.buildUpdate(attributes);
373
+ const res = await executeDatabaseQuery(kyselyQuery, 'execute');
374
+ const resultData = Array.from(res.entries())?.[0]?.[1];
375
+ return Number(resultData?.numUpdatedRows || 0);
376
+ }
377
+ /**
378
+ * persists any unsaved changes to the database. If a transaction
379
+ * is provided as a second argument, it will use that transaction
380
+ * to encapsulate the persisting of the dream, as well as any
381
+ * subsequent model hooks that are fired.
382
+ */
383
+ static async saveDream(dream, txn = null) {
384
+ const db = txn?.kyselyTransaction ?? _db('primary');
385
+ const sqlifiedAttributes = sqlAttributes(dream);
386
+ if (dream.isPersisted) {
387
+ const query = db
388
+ .updateTable(dream.table)
389
+ .set(sqlifiedAttributes)
390
+ .where(namespaceColumn(dream.primaryKey, dream.table), '=', dream.primaryKeyValue);
391
+ return await executeDatabaseQuery(query.returning([...dream.columns()]), 'executeTakeFirstOrThrow');
392
+ }
393
+ else {
394
+ const query = db
395
+ .insertInto(dream.table)
396
+ .values(sqlifiedAttributes)
397
+ .returning([...dream.columns()]);
398
+ return await executeDatabaseQuery(query, 'executeTakeFirstOrThrow');
399
+ }
400
+ }
401
+ dbConnectionType(sqlCommandType) {
402
+ if (this.dreamTransaction)
403
+ return 'primary';
404
+ switch (sqlCommandType) {
405
+ case 'select':
406
+ return this.connectionOverride || (this.isReplicaSafe() ? 'replica' : 'primary');
407
+ default:
408
+ return 'primary';
409
+ }
410
+ }
411
+ isReplicaSafe() {
412
+ return this.innerJoinDreamClasses.reduce((accumulator, dreamClass) => accumulator && dreamClass['replicaSafe'], this.dreamClass['replicaSafe']);
413
+ }
414
+ /**
415
+ * Returns the sql that would be executed by this Query
416
+ *
417
+ * ```ts
418
+ * User.where({ email: 'how@yadoin' }).sql()
419
+ * // {
420
+ * // query: {
421
+ * // kind: 'SelectQueryNode',
422
+ * // from: { kind: 'FromNode', froms: [Array] },
423
+ * // selections: [ [Object] ],
424
+ * // distinctOn: undefined,
425
+ * // joins: undefined,
426
+ * // groupBy: undefined,
427
+ * // orderBy: undefined,
428
+ * // where: { kind: 'WhereNode', where: [Object] },
429
+ * // frontModifiers: undefined,
430
+ * // endModifiers: undefined,
431
+ * // limit: undefined,
432
+ * // offset: undefined,
433
+ * // with: undefined,
434
+ * // having: undefined,
435
+ * // explain: undefined,
436
+ * // setOperations: undefined
437
+ * // },
438
+ * // sql: 'select "users".* from "users" where ("users"."email" = $1 and "users"."deleted_at" is null)',
439
+ * // parameters: [ 'how@yadoin' ]
440
+ * //}
441
+ * ```
442
+ *
443
+ * @returns An object representing the underlying sql statement
444
+ *
445
+ */
446
+ sql() {
447
+ const kyselyQuery = this.buildSelect();
448
+ return kyselyQuery.compile();
449
+ }
450
+ buildDelete() {
451
+ const kyselyQuery = this.dbFor('delete').deleteFrom(this.query['baseSqlAlias']);
452
+ const results = this.attachLimitAndOrderStatementsToNonSelectQuery(kyselyQuery);
453
+ return new KyselyQueryDriver(results.clone).buildCommon(results.kyselyQuery);
454
+ }
455
+ buildSelect({ bypassSelectAll = false, bypassOrder = false, columns, } = {}) {
456
+ let kyselyQuery;
457
+ if (this.query['baseSelectQuery']) {
458
+ const connectionOverride = this.query['connectionOverride'];
459
+ const query = connectionOverride
460
+ ? this.query['baseSelectQuery'].connection(connectionOverride)
461
+ : this.query['baseSelectQuery'];
462
+ kyselyQuery = new KyselyQueryDriver(query).buildSelect({ bypassSelectAll: true });
463
+ }
464
+ else {
465
+ const from = this.query['baseSqlAlias'] === this.query['tableName']
466
+ ? this.query['tableName']
467
+ : `${this.query['tableName']} as ${this.query['baseSqlAlias']}`;
468
+ kyselyQuery = this.dbFor('select').selectFrom(from);
469
+ }
470
+ if (this.query['distinctColumn']) {
471
+ kyselyQuery = kyselyQuery.distinctOn(this.query['distinctColumn']);
472
+ }
473
+ kyselyQuery = this.buildCommon(kyselyQuery);
474
+ kyselyQuery = this.conditionallyAttachSimilarityColumnsToSelect(kyselyQuery, {
475
+ bypassOrder: bypassOrder || !!this.query['distinctColumn'],
476
+ });
477
+ if (this.query['orderStatements'].length && !bypassOrder) {
478
+ this.query['orderStatements'].forEach(orderStatement => {
479
+ kyselyQuery = kyselyQuery.orderBy(this.namespaceColumn(orderStatement.column), orderByDirection(orderStatement.direction));
480
+ });
481
+ }
482
+ if (this.query['limitStatement'])
483
+ kyselyQuery = kyselyQuery.limit(this.query['limitStatement']);
484
+ if (this.query['offsetStatement'])
485
+ kyselyQuery = kyselyQuery.offset(this.query['offsetStatement']);
486
+ if (columns) {
487
+ kyselyQuery = kyselyQuery.select(this.columnsWithRequiredLoadColumns(columns).map(column => this.namespaceColumn(column)));
488
+ }
489
+ else if (!bypassSelectAll) {
490
+ kyselyQuery = kyselyQuery.selectAll(this.query['baseSqlAlias']);
491
+ }
492
+ // even though we manually bypass explicit order statements above,
493
+ // associations can contain their own ordering systems. If we do not
494
+ // escape all orders, we can mistakenly allow an order clause to sneak in.
495
+ if (bypassOrder)
496
+ kyselyQuery = kyselyQuery.clearOrderBy();
497
+ return kyselyQuery;
498
+ }
499
+ buildUpdate(attributes) {
500
+ let kyselyQuery = this.dbFor('update')
501
+ .updateTable(this.query['tableName'])
502
+ .set(attributes);
503
+ kyselyQuery = this.conditionallyAttachSimilarityColumnsToUpdate(kyselyQuery);
504
+ const results = this.attachLimitAndOrderStatementsToNonSelectQuery(kyselyQuery);
505
+ return new KyselyQueryDriver(results.clone).buildCommon(results.kyselyQuery);
506
+ }
507
+ /**
508
+ * @internal
509
+ *
510
+ * Used to hydrate dreams with the provided associations
511
+ */
512
+ hydrateAssociation(dreams, association, preloadedDreamsAndWhatTheyPointTo) {
513
+ switch (association.type) {
514
+ case 'HasMany':
515
+ dreams.forEach((dream) => {
516
+ dream[association.as] = [];
517
+ });
518
+ break;
519
+ default:
520
+ dreams.forEach((dream) => {
521
+ dream[associationToGetterSetterProp(association)] = null;
522
+ });
523
+ }
524
+ // dreams is a Rating
525
+ // Rating belongs to: rateables (Posts / Compositions)
526
+ // loadedAssociations is an array of Posts and Compositions
527
+ // if rating.rateable_id === loadedAssociation.primaryKeyvalue
528
+ // rating.rateable = loadedAssociation
529
+ preloadedDreamsAndWhatTheyPointTo.forEach(preloadedDreamAndWhatItPointsTo => {
530
+ dreams
531
+ .filter(dream => dream.primaryKeyValue === preloadedDreamAndWhatItPointsTo.pointsToPrimaryKey)
532
+ .forEach((dream) => {
533
+ if (association.type === 'HasMany') {
534
+ dream[association.as].push(preloadedDreamAndWhatItPointsTo.dream);
535
+ }
536
+ else {
537
+ // in a HasOne context, order clauses will be applied in advance,
538
+ // prior to hydration. Considering, we only want to set the first
539
+ // result and ignore other results, so we will use ||= to set.
540
+ dream[association.as] ||= preloadedDreamAndWhatItPointsTo.dream;
541
+ }
542
+ });
543
+ });
544
+ if (association.type === 'HasMany') {
545
+ dreams.forEach((dream) => Object.freeze(dream[association.as]));
546
+ }
547
+ }
548
+ /**
549
+ * @internal
550
+ *
551
+ * Used by loadBuider
552
+ */
553
+ async hydratePreload(dream) {
554
+ await this.applyPreload(this.query['preloadStatements'], this.query['preloadOnStatements'], dream);
555
+ }
556
+ static async duplicateDatabase() {
557
+ const dreamApp = DreamApp.getOrFail();
558
+ const parallelTests = dreamApp.parallelTests;
559
+ if (!parallelTests)
560
+ return;
561
+ DreamCLI.logger.logStartProgress(`duplicating db for parallel tests...`);
562
+ const dbConf = dreamApp.dbConnectionConfig('primary');
563
+ const client = await loadPgClient({ useSystemDb: true });
564
+ if (EnvInternal.boolean('DREAM_CORE_DEVELOPMENT')) {
565
+ const replicaTestWorkerDatabaseName = `replica_test_${dbConf.name}`;
566
+ DreamCLI.logger.logContinueProgress(`creating fake replica test database ${replicaTestWorkerDatabaseName}...`, { logPrefix: ' ├ [db]', logPrefixColor: 'cyan' });
567
+ await client.query(`DROP DATABASE IF EXISTS ${replicaTestWorkerDatabaseName};`);
568
+ await client.query(`CREATE DATABASE ${replicaTestWorkerDatabaseName} TEMPLATE ${dbConf.name};`);
569
+ }
570
+ for (let i = 2; i <= parallelTests; i++) {
571
+ const workerDatabaseName = `${dbConf.name}_${i}`;
572
+ DreamCLI.logger.logContinueProgress(`creating duplicate test database ${workerDatabaseName} for concurrent tests...`, { logPrefix: ' ├ [db]', logPrefixColor: 'cyan' });
573
+ await client.query(`DROP DATABASE IF EXISTS ${workerDatabaseName};`);
574
+ await client.query(`CREATE DATABASE ${workerDatabaseName} TEMPLATE ${dbConf.name};`);
575
+ }
576
+ await client.end();
577
+ DreamCLI.logger.logEndProgress();
578
+ }
579
+ aliasWhereStatements(whereStatements, alias) {
580
+ return whereStatements.map(whereStatement => this.aliasWhereStatement(whereStatement, alias));
581
+ }
582
+ aliasWhereStatement(whereStatement, alias) {
583
+ return Object.keys(whereStatement).reduce((aliasedWhere, key) => {
584
+ aliasedWhere[this.namespaceColumn(key, alias)] = whereStatement[key];
585
+ return aliasedWhere;
586
+ }, {});
587
+ }
588
+ rawifiedSelfOnClause({ associationAlias, selfAlias, selfAndClause, }) {
589
+ const alphanumericUnderscoreRegexp = /[^a-zA-Z0-9_]/g;
590
+ selfAlias = selfAlias.replace(alphanumericUnderscoreRegexp, '');
591
+ return Object.keys(selfAndClause).reduce((acc, key) => {
592
+ const selfColumn = selfAndClause[key]?.replace(alphanumericUnderscoreRegexp, '');
593
+ if (!selfColumn)
594
+ return acc;
595
+ acc[this.namespaceColumn(key, associationAlias)] = sql.raw(`"${snakeify(selfAlias)}"."${snakeify(selfColumn)}"`);
596
+ return acc;
597
+ }, {});
598
+ }
599
+ attachLimitAndOrderStatementsToNonSelectQuery(kyselyQuery) {
600
+ if (this.query['limitStatement'] || this.query['orderStatements'].length) {
601
+ kyselyQuery = kyselyQuery.where((eb) => {
602
+ const subquery = this.query.nestedSelect(this.dreamInstance.primaryKey);
603
+ return eb(this.dreamInstance.primaryKey, 'in', subquery);
604
+ });
605
+ return {
606
+ kyselyQuery,
607
+ clone: this.query.clone({
608
+ where: null,
609
+ whereNot: null,
610
+ order: null,
611
+ limit: null,
612
+ }),
613
+ };
614
+ }
615
+ return { kyselyQuery, clone: this.query };
616
+ }
617
+ columnsWithRequiredLoadColumns(columns) {
618
+ return uniq(compact([this.dreamClass.primaryKey, this.dreamClass['isSTIBase'] ? 'type' : null, ...columns]));
619
+ }
620
+ /**
621
+ * @internal
622
+ *
623
+ */
624
+ async executeJoinLoad(options = {}) {
625
+ const query = this.query['limit'](null).offset(null);
626
+ let kyselyQuery = new KyselyQueryDriver(query).buildSelect({ bypassSelectAll: true });
627
+ const aliasToDreamClassesMap = {
628
+ [this.query['baseSqlAlias']]: this.dreamClass,
629
+ ...this.joinStatementsToDreamClassesMap(this.query['leftJoinStatements']),
630
+ };
631
+ const associationAliasToColumnAliasMap = {};
632
+ const aliasToAssociationsMap = this.joinStatementsToAssociationsMap(this.query['leftJoinStatements']);
633
+ const aliases = Object.keys(aliasToDreamClassesMap);
634
+ let nextColumnAliasCounter = 0;
635
+ aliases.forEach((aliasOrExpression) => {
636
+ const alias = extractAssociationMetadataFromAssociationName(aliasOrExpression).alias;
637
+ if (alias === undefined)
638
+ throw new UnexpectedUndefined();
639
+ associationAliasToColumnAliasMap[alias] ||= {};
640
+ const aliasedDreamClass = aliasToDreamClassesMap[alias];
641
+ if (aliasedDreamClass === undefined)
642
+ throw new UnexpectedUndefined();
643
+ const association = aliasToAssociationsMap[alias];
644
+ const columns = alias === this.query['baseSqlAlias']
645
+ ? options.columns
646
+ ? this.columnsWithRequiredLoadColumns(options.columns)
647
+ : this.dreamClass.columns()
648
+ : aliasedDreamClass.columns();
649
+ columns.forEach((column) => {
650
+ const columnAlias = `dr${nextColumnAliasCounter++}`;
651
+ kyselyQuery = kyselyQuery.select(`${this.namespaceColumn(column, alias)} as ${columnAlias}`);
652
+ const columnAliasMap = associationAliasToColumnAliasMap[alias];
653
+ if (columnAliasMap === undefined)
654
+ throw new UnexpectedUndefined();
655
+ columnAliasMap[column] = columnAlias;
656
+ });
657
+ if (association?.type === 'HasOne' || association?.type === 'HasMany') {
658
+ const setupPreloadData = (dbColumnName) => {
659
+ const columnAlias = `dr${nextColumnAliasCounter++}`;
660
+ const columnAliasMap = associationAliasToColumnAliasMap[association.through];
661
+ if (columnAliasMap === undefined)
662
+ throw new UnexpectedUndefined();
663
+ columnAliasMap[dbColumnName] = columnAlias;
664
+ kyselyQuery = kyselyQuery.select(`${this.namespaceColumn(dbColumnName, association.through)} as ${columnAlias}`);
665
+ };
666
+ if (association.through && association.preloadThroughColumns) {
667
+ if (isObject(association.preloadThroughColumns)) {
668
+ const preloadMap = association.preloadThroughColumns;
669
+ Object.keys(preloadMap).forEach(columnName => setupPreloadData(columnName));
670
+ }
671
+ else {
672
+ const preloadArray = association.preloadThroughColumns;
673
+ preloadArray.forEach(columnName => setupPreloadData(columnName));
674
+ }
675
+ }
676
+ }
677
+ });
678
+ const queryResults = await executeDatabaseQuery(kyselyQuery, 'execute');
679
+ const aliasToDreamIdMap = queryResults.reduce((aliasToDreamIdMap, singleSqlResult) => {
680
+ this.fleshOutJoinLoadExecutionResults({
681
+ currentAlias: this.query['baseSqlAlias'],
682
+ singleSqlResult,
683
+ aliasToDreamIdMap,
684
+ associationAliasToColumnAliasMap,
685
+ aliasToAssociationsMap,
686
+ aliasToDreamClassesMap,
687
+ leftJoinStatements: this.query['leftJoinStatements'],
688
+ });
689
+ return aliasToDreamIdMap;
690
+ }, {});
691
+ const baseModelIdToDreamMap = aliasToDreamIdMap[this.query['baseSqlAlias']] || new Map();
692
+ return compact(Array.from(baseModelIdToDreamMap.values()));
693
+ }
694
+ fleshOutJoinLoadExecutionResults({ currentAlias, singleSqlResult, aliasToDreamIdMap, associationAliasToColumnAliasMap, aliasToAssociationsMap, aliasToDreamClassesMap, leftJoinStatements, }) {
695
+ const dreamClass = aliasToDreamClassesMap[currentAlias];
696
+ if (dreamClass === undefined)
697
+ throw new UnexpectedUndefined();
698
+ const columnToColumnAliasMap = associationAliasToColumnAliasMap[currentAlias];
699
+ if (columnToColumnAliasMap === undefined)
700
+ throw new UnexpectedUndefined();
701
+ const primaryKeyName = dreamClass.primaryKey;
702
+ if (primaryKeyName === undefined)
703
+ throw new UnexpectedUndefined();
704
+ const columnAlias = columnToColumnAliasMap[primaryKeyName];
705
+ if (columnAlias === undefined)
706
+ throw new UnexpectedUndefined();
707
+ const primaryKeyValue = singleSqlResult[columnAlias];
708
+ if (!primaryKeyValue)
709
+ return null;
710
+ aliasToDreamIdMap[currentAlias] ||= new Map();
711
+ if (!aliasToDreamIdMap[currentAlias].get(primaryKeyValue)) {
712
+ const columnValueMap = Object.keys(columnToColumnAliasMap).reduce((columnNameValueMap, columnName) => {
713
+ const columnAlias = columnToColumnAliasMap[columnName];
714
+ if (columnAlias === undefined)
715
+ throw new UnexpectedUndefined();
716
+ columnNameValueMap[columnName] = singleSqlResult[columnAlias];
717
+ return columnNameValueMap;
718
+ }, {});
719
+ const dream = this.dbResultToDreamInstance(columnValueMap, dreamClass);
720
+ const association = aliasToAssociationsMap[currentAlias];
721
+ if (association && association.through && association.preloadThroughColumns) {
722
+ const throughAssociationColumnToColumnAliasMap = associationAliasToColumnAliasMap[association.through];
723
+ if (throughAssociationColumnToColumnAliasMap === undefined)
724
+ throw new UnexpectedUndefined();
725
+ this.hydratePreloadedThroughColumns({
726
+ association,
727
+ columnToColumnAliasMap: throughAssociationColumnToColumnAliasMap,
728
+ dream,
729
+ singleSqlResult,
730
+ });
731
+ }
732
+ aliasToDreamIdMap[protectAgainstPollutingAssignment(currentAlias)]?.set(primaryKeyValue, dream);
733
+ }
734
+ const dream = aliasToDreamIdMap[currentAlias].get(primaryKeyValue);
735
+ Object.keys(leftJoinStatements).forEach(nextAlias => {
736
+ const { name: associationName, alias } = extractAssociationMetadataFromAssociationName(nextAlias);
737
+ const association = dreamClass['getAssociationMetadata'](associationName);
738
+ if (association === undefined)
739
+ throw new UnexpectedUndefined();
740
+ const associatedDream = this.fleshOutJoinLoadExecutionResults({
741
+ currentAlias: alias,
742
+ singleSqlResult,
743
+ aliasToDreamIdMap,
744
+ associationAliasToColumnAliasMap,
745
+ aliasToAssociationsMap,
746
+ aliasToDreamClassesMap,
747
+ leftJoinStatements: leftJoinStatements[nextAlias],
748
+ });
749
+ const hasMany = association.type === 'HasMany';
750
+ // initialize by trying to access the association, which throws an exception if not yet initialized
751
+ try {
752
+ ;
753
+ dream[association.as];
754
+ }
755
+ catch {
756
+ if (hasMany)
757
+ dream[association.as] = [];
758
+ else
759
+ dream[associationToGetterSetterProp(association)] = null;
760
+ }
761
+ if (!associatedDream)
762
+ return;
763
+ if (hasMany) {
764
+ if (!dream[association.as].includes(associatedDream))
765
+ dream[association.as].push(associatedDream);
766
+ }
767
+ else
768
+ dream[associationToGetterSetterProp(association)] = associatedDream;
769
+ });
770
+ return dream;
771
+ }
772
+ hydratePreloadedThroughColumns({ association, columnToColumnAliasMap, dream, singleSqlResult, }) {
773
+ if (!association.through)
774
+ return;
775
+ if (!dream.preloadedThroughColumns)
776
+ return;
777
+ let columnNames = [];
778
+ const columnNameToPreloadedThroughColumnNameMap = {};
779
+ if (isObject(association.preloadThroughColumns)) {
780
+ const preloadMap = association.preloadThroughColumns;
781
+ columnNames = Object.keys(preloadMap).map(columnName => {
782
+ columnNameToPreloadedThroughColumnNameMap[columnName] = preloadMap[columnName];
783
+ return columnName;
784
+ });
785
+ }
786
+ else if (Array.isArray(association.preloadThroughColumns)) {
787
+ columnNames = association.preloadThroughColumns.map(columnName => {
788
+ columnNameToPreloadedThroughColumnNameMap[columnName] = columnName;
789
+ return columnName;
790
+ });
791
+ }
792
+ columnNames.forEach(columnName => {
793
+ const preloadedThroughColumnName = columnNameToPreloadedThroughColumnNameMap[columnName];
794
+ if (preloadedThroughColumnName === undefined)
795
+ throw new UnexpectedUndefined();
796
+ const columnAlias = columnToColumnAliasMap[columnName];
797
+ if (columnAlias === undefined) {
798
+ throw new UnexpectedUndefined();
799
+ }
800
+ ;
801
+ dream.preloadedThroughColumns[preloadedThroughColumnName] = singleSqlResult[columnAlias];
802
+ });
803
+ }
804
+ applyJoinAndStatement(join, joinAndStatement, rootTableOrAssociationAlias) {
805
+ if (!joinAndStatement)
806
+ return join;
807
+ join = this._applyJoinAndStatements(join, joinAndStatement.and, rootTableOrAssociationAlias);
808
+ join = this._applyJoinAndStatements(join, joinAndStatement.andNot, rootTableOrAssociationAlias, {
809
+ negate: true,
810
+ });
811
+ join = this._applyJoinAndAnyStatements(join, joinAndStatement.andAny, rootTableOrAssociationAlias);
812
+ return join;
813
+ }
814
+ _applyJoinAndStatements(join, joinAndStatement, rootTableOrAssociationAlias, { negate = false, } = {}) {
815
+ if (!joinAndStatement)
816
+ return join;
817
+ return join.on((eb) => this.joinAndStatementToExpressionWrapper(joinAndStatement, rootTableOrAssociationAlias, eb, {
818
+ negate,
819
+ disallowSimilarityOperator: negate,
820
+ }));
821
+ }
822
+ _applyJoinAndAnyStatements(join, joinAndAnyStatement, rootTableOrAssociationAlias) {
823
+ if (!joinAndAnyStatement)
824
+ return join;
825
+ if (!joinAndAnyStatement.length)
826
+ return join;
827
+ return join.on((eb) => {
828
+ return eb.or(joinAndAnyStatement.map(joinAndStatement => this.joinAndStatementToExpressionWrapper(joinAndStatement, rootTableOrAssociationAlias, eb)));
829
+ });
830
+ }
831
+ joinAndStatementToExpressionWrapper(joinAndStatement, rootTableOrAssociationAlias, eb, { negate = false, disallowSimilarityOperator = true, } = {}) {
832
+ return this.whereStatementToExpressionWrapper(eb, Object.keys(joinAndStatement).reduce((agg, key) => {
833
+ agg[this.namespaceColumn(key.toString(), rootTableOrAssociationAlias)] = joinAndStatement[key];
834
+ return agg;
835
+ }, {}), {
836
+ negate,
837
+ disallowSimilarityOperator,
838
+ });
839
+ }
840
+ buildCommon(kyselyQuery) {
841
+ this.checkForQueryViolations();
842
+ const query = this.conditionallyApplyDefaultScopes();
843
+ if (!isEmpty(query['innerJoinStatements'])) {
844
+ kyselyQuery = this.recursivelyJoin({
845
+ query: kyselyQuery,
846
+ joinStatement: query['innerJoinStatements'],
847
+ joinAndStatements: query['innerJoinAndStatements'],
848
+ dreamClass: query['dreamClass'],
849
+ previousAssociationTableOrAlias: this.query['baseSqlAlias'],
850
+ joinType: 'inner',
851
+ });
852
+ }
853
+ if (!isEmpty(query['leftJoinStatements'])) {
854
+ kyselyQuery = this.recursivelyJoin({
855
+ query: kyselyQuery,
856
+ joinStatement: query['leftJoinStatements'],
857
+ joinAndStatements: query['leftJoinAndStatements'],
858
+ dreamClass: query['dreamClass'],
859
+ previousAssociationTableOrAlias: this.query['baseSqlAlias'],
860
+ joinType: 'left',
861
+ });
862
+ }
863
+ if (query['whereStatements'].length ||
864
+ query['whereNotStatements'].length ||
865
+ query['whereAnyStatements'].length) {
866
+ kyselyQuery = kyselyQuery.where((eb) => eb.and([
867
+ ...this.aliasWhereStatements(query['whereStatements'], query['baseSqlAlias']).map(whereStatement => this.whereStatementToExpressionWrapper(eb, whereStatement, {
868
+ disallowSimilarityOperator: false,
869
+ })),
870
+ ...this.aliasWhereStatements(query['whereNotStatements'], query['baseSqlAlias']).map(whereNotStatement => this.whereStatementToExpressionWrapper(eb, whereNotStatement, { negate: true })),
871
+ ...query['whereAnyStatements'].map(whereAnyStatements => eb.or(this.aliasWhereStatements(whereAnyStatements, query['baseSqlAlias']).map(whereAnyStatement => this.whereStatementToExpressionWrapper(eb, whereAnyStatement)))),
872
+ ]));
873
+ }
874
+ return kyselyQuery;
875
+ }
876
+ whereStatementToExpressionWrapper(eb, whereStatement, { negate = false, disallowSimilarityOperator = true, } = {}) {
877
+ const clauses = compact(Object.keys(whereStatement)
878
+ .filter(key => whereStatement[key] !== DreamConst.required)
879
+ .map(attr => {
880
+ const val = whereStatement[attr];
881
+ if (val?.isOpsStatement &&
882
+ val.shouldBypassWhereStatement) {
883
+ if (disallowSimilarityOperator)
884
+ throw new Error('Similarity operator may not be used in whereAny');
885
+ // some ops statements are handled specifically in the select portion of the query,
886
+ // and should be ommited from the where clause directly
887
+ return;
888
+ }
889
+ const { a, b, c, a2, b2, c2 } = this.dreamWhereStatementToExpressionBuilderParts(attr, val);
890
+ // postgres is unable to handle WHERE IN statements with blank arrays, such as in
891
+ // "WHERE id IN ()", meaning that:
892
+ // 1. If we receive a blank array during an IN comparison,
893
+ // then we need to simply regurgitate a where statement which
894
+ // guarantees no records.
895
+ // 2. If we receive a blank array during a NOT IN comparison,
896
+ // then it is the same as the where statement not being present at all,
897
+ // resulting in a noop on our end
898
+ //
899
+ if (Array.isArray(c)) {
900
+ if ((b === 'in' && c.includes(null)) || (b === 'not in' && !c.includes(null))) {
901
+ return this.inArrayWithNull_or_notInArrayWithoutNull_ExpressionBuilder(eb, a, b, c);
902
+ }
903
+ else if (negate && b === 'in' && !c.includes(null)) {
904
+ return this.inArrayWithoutNullExpressionBuilder(eb, a, b, c);
905
+ }
906
+ else if (b === 'not in' && c.includes(null)) {
907
+ return this.notInArrayWithNullExpressionBuilder(eb, a, b, c);
908
+ }
909
+ const compactedC = compact(c);
910
+ if (b === 'in' && compactedC.length === 0) {
911
+ // in an empty array means match nothing
912
+ return sql `FALSE`;
913
+ }
914
+ else if (b === 'not in' && compactedC.length === 0) {
915
+ // not in an empty array means match everything
916
+ return sql `TRUE`;
917
+ }
918
+ else {
919
+ return eb(a, b, compactedC);
920
+ }
921
+ //
922
+ }
923
+ else if (b === '=' && c === null) {
924
+ return eb(a, 'is', null);
925
+ //
926
+ }
927
+ else if (b === '!=' && c === null) {
928
+ return eb(a, 'is not', null);
929
+ //
930
+ }
931
+ else if (b === '=' && negate) {
932
+ return eb.and([eb(a, '=', c), eb(a, 'is not', null)]);
933
+ //
934
+ }
935
+ else if (b === '!=' && c !== null) {
936
+ return eb.or([eb(a, '!=', c), eb(a, 'is', null)]);
937
+ //
938
+ }
939
+ else {
940
+ const expression = eb(a, b, c);
941
+ if (b2)
942
+ return expression.and(eb(a2, b2, c2));
943
+ return expression;
944
+ }
945
+ }));
946
+ return negate ? eb.not(eb.parens(eb.and(clauses))) : eb.and(clauses);
947
+ }
948
+ inArrayWithNull_or_notInArrayWithoutNull_ExpressionBuilder(eb, a, b, c) {
949
+ const isNullStatement = eb(a, 'is', null);
950
+ const compactedC = compact(c);
951
+ if (compactedC.length)
952
+ return eb.or([eb(a, b, compactedC), isNullStatement]);
953
+ // not in an empty array means match everything
954
+ if (b === 'not in')
955
+ return sql `TRUE`;
956
+ return isNullStatement;
957
+ }
958
+ inArrayWithoutNullExpressionBuilder(eb, a, b, c) {
959
+ const isNotNullStatement = eb(a, 'is not', null);
960
+ const compactedC = compact(c);
961
+ if (compactedC.length)
962
+ return eb.and([eb(a, 'in', compactedC), isNotNullStatement]);
963
+ // in an empty array means match nothing
964
+ return sql `FALSE`;
965
+ }
966
+ notInArrayWithNullExpressionBuilder(eb, a, b, c) {
967
+ const isNullStatement = eb(a, 'is not', null);
968
+ const compactedC = compact(c);
969
+ if (compactedC.length)
970
+ return eb.and([eb(a, 'not in', compactedC), isNullStatement]);
971
+ return isNullStatement;
972
+ }
973
+ dreamWhereStatementToExpressionBuilderParts(attr, val) {
974
+ let a;
975
+ let b;
976
+ let c;
977
+ let a2 = null;
978
+ let b2 = null;
979
+ let c2 = null;
980
+ if (val instanceof Function && val !== DreamConst.passthrough) {
981
+ val = val();
982
+ }
983
+ else if (val === DreamConst.passthrough) {
984
+ const column = attr.split('.').at(-1);
985
+ if (this.query['passthroughOnStatement'][column] === undefined)
986
+ throw new MissingRequiredPassthroughForAssociationAndClause(column);
987
+ val = this.query['passthroughOnStatement'][column];
988
+ }
989
+ if (val === null) {
990
+ a = attr;
991
+ b = 'is';
992
+ c = val;
993
+ }
994
+ else if (['SelectQueryBuilder', 'SelectQueryBuilderImpl'].includes(val?.constructor?.name)) {
995
+ a = attr;
996
+ b = 'in';
997
+ c = val;
998
+ }
999
+ else if (Array.isArray(val)) {
1000
+ a = attr;
1001
+ b = 'in';
1002
+ c = val.map(v => v instanceof DateTime || v instanceof CalendarDate
1003
+ ? v.toSQL()
1004
+ : typeof v === 'string'
1005
+ ? normalizeUnicode(v)
1006
+ : v);
1007
+ }
1008
+ else if (val instanceof CurriedOpsStatement) {
1009
+ val = val.toOpsStatement(this.dreamClass, attr);
1010
+ a = attr;
1011
+ b = val.operator;
1012
+ c = val.value;
1013
+ }
1014
+ else if (val instanceof OpsStatement) {
1015
+ a = attr;
1016
+ b = val.operator;
1017
+ c = val.value;
1018
+ }
1019
+ else if (val instanceof Range) {
1020
+ const rangeStart = val.begin;
1021
+ const rangeEnd = val.end;
1022
+ const excludeEnd = val.excludeEnd;
1023
+ if (rangeStart && rangeEnd) {
1024
+ a = attr;
1025
+ b = '>=';
1026
+ c = rangeStart;
1027
+ a2 = attr;
1028
+ b2 = excludeEnd ? '<' : '<=';
1029
+ c2 = rangeEnd;
1030
+ }
1031
+ else if (rangeStart) {
1032
+ a = attr;
1033
+ b = '>=';
1034
+ c = rangeStart;
1035
+ }
1036
+ else {
1037
+ a = attr;
1038
+ b = excludeEnd ? '<' : '<=';
1039
+ c = rangeEnd;
1040
+ }
1041
+ }
1042
+ else {
1043
+ a = attr;
1044
+ b = '=';
1045
+ c = val;
1046
+ }
1047
+ if (c instanceof DateTime || c instanceof CalendarDate)
1048
+ c = c.toSQL();
1049
+ else if (typeof c === 'string')
1050
+ c = normalizeUnicode(c);
1051
+ if (c2 instanceof DateTime || c2 instanceof CalendarDate)
1052
+ c2 = c2.toSQL();
1053
+ else if (typeof c2 === 'string')
1054
+ c2 = normalizeUnicode(c2);
1055
+ if (a && c === undefined)
1056
+ throw new CannotPassUndefinedAsAValueToAWhereClause(this.dreamClass, a);
1057
+ if (a2 && c2 === undefined)
1058
+ throw new CannotPassUndefinedAsAValueToAWhereClause(this.dreamClass, a2);
1059
+ return { a, b, c, a2, b2, c2 };
1060
+ }
1061
+ recursivelyJoin({ query, joinStatement, joinAndStatements, dreamClass, previousAssociationTableOrAlias, joinType, }) {
1062
+ for (const currentAssociationTableOrAlias of Object.keys(joinStatement)) {
1063
+ const results = this.applyOneJoin({
1064
+ query,
1065
+ dreamClass,
1066
+ previousAssociationTableOrAlias,
1067
+ currentAssociationTableOrAlias,
1068
+ joinAndStatements,
1069
+ joinType,
1070
+ });
1071
+ query = results.query;
1072
+ const association = results.association;
1073
+ query = this.recursivelyJoin({
1074
+ query,
1075
+ joinStatement: joinStatement[currentAssociationTableOrAlias],
1076
+ joinAndStatements: joinAndStatements[currentAssociationTableOrAlias],
1077
+ dreamClass: association.modelCB(),
1078
+ previousAssociationTableOrAlias: currentAssociationTableOrAlias,
1079
+ joinType,
1080
+ });
1081
+ }
1082
+ return query;
1083
+ }
1084
+ /**
1085
+ * @internal
1086
+ *
1087
+ *
1088
+ */
1089
+ joinStatementsToDreamClassesMap(joinStatements) {
1090
+ const associationsToDreamClassesMap = {};
1091
+ objectPathsToArrays(joinStatements).forEach(associationChain => this.associationNamesToDreamClassesMap(associationChain, associationsToDreamClassesMap));
1092
+ return associationsToDreamClassesMap;
1093
+ }
1094
+ /**
1095
+ * @internal
1096
+ *
1097
+ *
1098
+ */
1099
+ joinStatementsToAssociationsMap(joinStatements) {
1100
+ const associationsToAssociationsMap = {};
1101
+ objectPathsToArrays(joinStatements).forEach(associationChain => this.associationNamesToAssociationsMap(associationChain, associationsToAssociationsMap));
1102
+ return associationsToAssociationsMap;
1103
+ }
1104
+ /**
1105
+ * @internal
1106
+ *
1107
+ *
1108
+ */
1109
+ associationNamesToDreamClassesMap(associationNames, associationsToDreamClassesMap = {}) {
1110
+ const namesToAssociationsAndDreamClasses = this.associationNamesToAssociationDataAndDreamClassesMap(associationNames);
1111
+ return Object.keys(namesToAssociationsAndDreamClasses).reduce((remap, associationName) => {
1112
+ const associationAndDreamClass = namesToAssociationsAndDreamClasses[associationName];
1113
+ if (associationAndDreamClass === undefined)
1114
+ throw new UnexpectedUndefined();
1115
+ remap[associationName] = associationAndDreamClass.dreamClass;
1116
+ return remap;
1117
+ }, associationsToDreamClassesMap);
1118
+ }
1119
+ /**
1120
+ * @internal
1121
+ *
1122
+ *
1123
+ */
1124
+ associationNamesToAssociationsMap(associationNames, associationsToAssociations = {}) {
1125
+ const namesToAssociationsAndDreamClasses = this.associationNamesToAssociationDataAndDreamClassesMap(associationNames);
1126
+ return Object.keys(namesToAssociationsAndDreamClasses).reduce((remap, associationName) => {
1127
+ const associationAndDreamClass = namesToAssociationsAndDreamClasses[associationName];
1128
+ if (associationAndDreamClass === undefined)
1129
+ throw new UnexpectedUndefined();
1130
+ remap[associationName] = associationAndDreamClass.association;
1131
+ return remap;
1132
+ }, associationsToAssociations);
1133
+ }
1134
+ /**
1135
+ * @internal
1136
+ */
1137
+ associationNamesToAssociationDataAndDreamClassesMap(associationNames) {
1138
+ const associationsToDreamClassesMap = {};
1139
+ associationNames.reduce((dreamClass, associationName) => {
1140
+ const { name, alias } = extractAssociationMetadataFromAssociationName(associationName);
1141
+ const association = dreamClass['getAssociationMetadata'](name);
1142
+ if (association === undefined)
1143
+ throw new UnexpectedUndefined();
1144
+ const through = association.through;
1145
+ if (through) {
1146
+ const { throughAssociation, throughAssociationDreamClass } = this.throughAssociationDetails(dreamClass, through);
1147
+ associationsToDreamClassesMap[through] = {
1148
+ association: throughAssociation,
1149
+ dreamClass: throughAssociationDreamClass,
1150
+ };
1151
+ }
1152
+ const nextDreamClass = association.modelCB();
1153
+ if (alias === undefined)
1154
+ throw new UnexpectedUndefined();
1155
+ associationsToDreamClassesMap[alias] = { association, dreamClass: nextDreamClass };
1156
+ return nextDreamClass;
1157
+ }, this.dreamClass);
1158
+ return associationsToDreamClassesMap;
1159
+ }
1160
+ /**
1161
+ * @internal
1162
+ */
1163
+ throughAssociationDetails(dreamClass, through) {
1164
+ const throughAssociation = dreamClass['getAssociationMetadata'](through);
1165
+ if (throughAssociation === undefined)
1166
+ throw new UnexpectedUndefined();
1167
+ const throughAssociationDreamClass = throughAssociation.modelCB();
1168
+ return { throughAssociation, throughAssociationDreamClass };
1169
+ }
1170
+ /**
1171
+ * @internal
1172
+ *
1173
+ * Returns a namespaced column name
1174
+ *
1175
+ * @returns A string
1176
+ */
1177
+ namespaceColumn(column, alias = this.query['baseSqlAlias']) {
1178
+ return namespaceColumn(column, alias);
1179
+ }
1180
+ checkForQueryViolations() {
1181
+ const invalidWhereNotClauses = this.similarityStatementBuilder().whereNotStatementsWithSimilarityClauses();
1182
+ if (invalidWhereNotClauses.length) {
1183
+ const invalidWhereNotClause = invalidWhereNotClauses[0];
1184
+ if (invalidWhereNotClause === undefined)
1185
+ throw new UnexpectedUndefined();
1186
+ const { tableName, columnName, opsStatement } = invalidWhereNotClause;
1187
+ throw new CannotNegateSimilarityClause(tableName, columnName, opsStatement.value);
1188
+ }
1189
+ }
1190
+ similarityStatementBuilder() {
1191
+ return new SimilarityBuilder(this.dreamInstance, {
1192
+ where: [...this.query['whereStatements']],
1193
+ whereNot: [...this.query['whereNotStatements']],
1194
+ joinAndStatements: this.query['innerJoinAndStatements'],
1195
+ transaction: this.query['dreamTransaction'],
1196
+ connection: this.query['connectionOverride'],
1197
+ });
1198
+ }
1199
+ /**
1200
+ * @internal
1201
+ *
1202
+ * Applies a preload statement
1203
+ */
1204
+ async applyPreload(preloadStatement, preloadOnStatements, dream) {
1205
+ const keys = Object.keys(preloadStatement);
1206
+ for (const key of keys) {
1207
+ const nestedDreams = await this.applyOnePreload(key, dream, this.applyablePreloadOnStatements(preloadOnStatements[key]));
1208
+ if (nestedDreams) {
1209
+ await this.applyPreload(preloadStatement[key], preloadOnStatements[key], nestedDreams);
1210
+ }
1211
+ }
1212
+ }
1213
+ /**
1214
+ * @internal
1215
+ *
1216
+ * retrieves on statements that can be applied to a preload
1217
+ */
1218
+ applyablePreloadOnStatements(preloadOnStatements) {
1219
+ if (preloadOnStatements === undefined)
1220
+ return undefined;
1221
+ return Object.keys(preloadOnStatements).reduce((agg, key) => {
1222
+ const value = preloadOnStatements[key];
1223
+ if (value === undefined)
1224
+ throw new UnexpectedUndefined();
1225
+ // filter out plain objects, but not ops and not and/andNot/andAny statements
1226
+ // because plain objects are just the next level of nested preload
1227
+ if (key === 'and' ||
1228
+ key === 'andNot' ||
1229
+ key === 'andAny' ||
1230
+ value === null ||
1231
+ value.constructor !== Object) {
1232
+ agg[key] = value;
1233
+ }
1234
+ return agg;
1235
+ }, {});
1236
+ }
1237
+ conditionallyApplyDefaultScopes() {
1238
+ if (this.query['bypassAllDefaultScopes'] || this.query['bypassAllDefaultScopesExceptOnAssociations'])
1239
+ return this.query;
1240
+ const thisScopes = this.dreamClass['scopes'].default;
1241
+ let query = this.query;
1242
+ for (const scope of thisScopes) {
1243
+ if (!shouldBypassDefaultScope(scope.method, {
1244
+ defaultScopesToBypass: [
1245
+ ...this.query['defaultScopesToBypass'],
1246
+ ...this.query['defaultScopesToBypassExceptOnAssociations'],
1247
+ ],
1248
+ })) {
1249
+ query = this.dreamClass[scope.method](query);
1250
+ }
1251
+ }
1252
+ return query;
1253
+ }
1254
+ /**
1255
+ * Each association in the chain is pushed onto `throughAssociations`
1256
+ * and `applyOneJoin` is recursively called. The trick is that the
1257
+ * through associations don't get written into the SQL; they
1258
+ * locate the next association we need to build into the SQL,
1259
+ * which is only run by the association that started the `through`
1260
+ * chain. The final association at the end of the `through` chain _is_
1261
+ * written into the SQL as a full association, but the modifications from
1262
+ * the `through` association are only added when the recursion returns
1263
+ * back to the association that kicked off the through associations.
1264
+ */
1265
+ joinsBridgeThroughAssociations({ query, dreamClass, association, previousAssociationTableOrAlias, throughAssociations, joinType, }) {
1266
+ if (association.type === 'BelongsTo' || !association.through) {
1267
+ return {
1268
+ query,
1269
+ dreamClass,
1270
+ association,
1271
+ previousAssociationTableOrAlias,
1272
+ };
1273
+ }
1274
+ else {
1275
+ throughAssociations.push(association);
1276
+ // We have entered joinsBridgeThroughAssociations with the
1277
+ // CompositionAssetAudits HasOne User association, which
1278
+ // is through compositionAsset
1279
+ // We now apply the compositionAsset association (a BelongsTo)
1280
+ // to the query
1281
+ const { query: queryWithThroughAssociationApplied } = this.applyOneJoin({
1282
+ query,
1283
+ dreamClass,
1284
+ previousAssociationTableOrAlias,
1285
+ currentAssociationTableOrAlias: association.through,
1286
+ throughAssociations,
1287
+ joinType,
1288
+ });
1289
+ // The through association has both a `through` and a `source`. The `source`
1290
+ // is the association on the model that has now been joined. In our example,
1291
+ // the `source` is the `user` association on the CompositionAsset model
1292
+ const { newAssociation, throughAssociation, throughClass } = this.followThroughAssociation(dreamClass, association);
1293
+ if (newAssociation.through) {
1294
+ // This new association is itself a through association, so we recursively
1295
+ // call joinsBridgeThroughAssociations
1296
+ return this.joinsBridgeThroughAssociations({
1297
+ query: queryWithThroughAssociationApplied,
1298
+ dreamClass: throughClass,
1299
+ association: newAssociation,
1300
+ previousAssociationTableOrAlias: throughAssociation.as,
1301
+ throughAssociations,
1302
+ joinType,
1303
+ });
1304
+ }
1305
+ else {
1306
+ // This new association is not a through association, so
1307
+ // this is the target association we were looking for
1308
+ return {
1309
+ query: queryWithThroughAssociationApplied,
1310
+ dreamClass: association.modelCB(),
1311
+ association: newAssociation,
1312
+ throughClass,
1313
+ previousAssociationTableOrAlias: association.through,
1314
+ };
1315
+ }
1316
+ }
1317
+ }
1318
+ /**
1319
+ * @internal
1320
+ *
1321
+ * Used to bridge through associations
1322
+ */
1323
+ followThroughAssociation(dreamClass, association) {
1324
+ const throughAssociation = association.through && dreamClass['getAssociationMetadata'](association.through);
1325
+ if (!throughAssociation)
1326
+ throw new MissingThroughAssociation({
1327
+ dreamClass,
1328
+ association,
1329
+ });
1330
+ const throughClass = throughAssociation.modelCB();
1331
+ if (Array.isArray(throughClass))
1332
+ throw new CannotAssociateThroughPolymorphic({
1333
+ dreamClass,
1334
+ association,
1335
+ });
1336
+ const newAssociation = getSourceAssociation(throughClass, association.source);
1337
+ if (!newAssociation)
1338
+ throw new MissingThroughAssociationSource({
1339
+ dreamClass,
1340
+ throughClass,
1341
+ association,
1342
+ });
1343
+ return { throughAssociation, throughClass, newAssociation };
1344
+ }
1345
+ /**
1346
+ * Each association in the chain is pushed onto `throughAssociations`
1347
+ * and `applyOneJoin` is recursively called. The trick is that the
1348
+ * through associations don't get written into the SQL; they
1349
+ * locate the next association we need to build into the SQL,
1350
+ * which is only run by the association that started the `through`
1351
+ * chain. The final association at the end of the `through` chain _is_
1352
+ * written into the SQL as a full association, but the modifications from
1353
+ * the `through` association are only added when the recursion returns
1354
+ * back to the association that kicked off the through associations.
1355
+ */
1356
+ applyOneJoin({ query, dreamClass, previousAssociationTableOrAlias, currentAssociationTableOrAlias, joinAndStatements = {}, throughAssociations = [], joinType, }) {
1357
+ const { name, alias } = extractAssociationMetadataFromAssociationName(currentAssociationTableOrAlias);
1358
+ const joinAndStatement = joinAndStatements[currentAssociationTableOrAlias];
1359
+ previousAssociationTableOrAlias = extractAssociationMetadataFromAssociationName(previousAssociationTableOrAlias).alias;
1360
+ currentAssociationTableOrAlias = alias;
1361
+ let association = dreamClass['getAssociationMetadata'](name);
1362
+ if (!association) {
1363
+ throw new JoinAttemptedOnMissingAssociation({
1364
+ dreamClass,
1365
+ associationName: currentAssociationTableOrAlias,
1366
+ });
1367
+ }
1368
+ const results = this.joinsBridgeThroughAssociations({
1369
+ query,
1370
+ dreamClass,
1371
+ association,
1372
+ previousAssociationTableOrAlias,
1373
+ throughAssociations,
1374
+ joinType,
1375
+ });
1376
+ query = results.query;
1377
+ dreamClass = results.dreamClass;
1378
+ association = results.association;
1379
+ const timeToApplyThroughAssociations = throughAssociations.length && throughAssociations[0]?.source === association.as;
1380
+ const originalPreviousAssociationTableOrAlias = previousAssociationTableOrAlias;
1381
+ previousAssociationTableOrAlias = results.previousAssociationTableOrAlias;
1382
+ const throughClass = results.throughClass;
1383
+ if (timeToApplyThroughAssociations) {
1384
+ /**
1385
+ * Each association in the chain is pushed onto `throughAssociations`
1386
+ * and `applyOneJoin` is recursively called. The trick is that the
1387
+ * through associations don't get written into the SQL; they
1388
+ * locate the next association we need to build into the SQL,
1389
+ * which is only run by the association that started the `through`
1390
+ * chain (thus the
1391
+ * `throughAssociations.length && throughAssociations[0].source === association.as`
1392
+ * above). The final association at the end of the `through` chain _is_
1393
+ * written into the SQL as a full association, but the modifications from
1394
+ * the `through` association are only added when the recursion returns
1395
+ * back to the association that kicked off the through associations.
1396
+ */
1397
+ throughAssociations.forEach((throughAssociation) => {
1398
+ if (throughAssociation.type === 'HasMany') {
1399
+ if (query?.distinctOn && throughAssociation.distinct) {
1400
+ query = query.distinctOn(this.distinctColumnNameForAssociation({
1401
+ association: throughAssociation,
1402
+ tableNameOrAlias: currentAssociationTableOrAlias,
1403
+ foreignKey: throughAssociation.primaryKey(),
1404
+ }));
1405
+ }
1406
+ if (throughAssociation.order) {
1407
+ query = this.applyOrderStatementForAssociation({
1408
+ query,
1409
+ association: throughAssociation,
1410
+ tableNameOrAlias: currentAssociationTableOrAlias,
1411
+ });
1412
+ }
1413
+ }
1414
+ });
1415
+ }
1416
+ if (association.type === 'BelongsTo') {
1417
+ if (Array.isArray(association.modelCB()))
1418
+ throw new CannotJoinPolymorphicBelongsToError({
1419
+ dreamClass,
1420
+ association,
1421
+ innerJoinStatements: this.query['innerJoinStatements'],
1422
+ leftJoinStatements: this.query['leftJoinStatements'],
1423
+ });
1424
+ const to = association.modelCB().table;
1425
+ const joinTableExpression = currentAssociationTableOrAlias === to
1426
+ ? currentAssociationTableOrAlias
1427
+ : `${to} as ${currentAssociationTableOrAlias}`;
1428
+ query = query[(joinType === 'inner' ? 'innerJoin' : 'leftJoin')](joinTableExpression, (join) => {
1429
+ join = join.onRef(this.namespaceColumn(association.foreignKey(), previousAssociationTableOrAlias), '=', this.namespaceColumn(association.primaryKey(), currentAssociationTableOrAlias));
1430
+ if (timeToApplyThroughAssociations) {
1431
+ throughAssociations.forEach((throughAssociation) => {
1432
+ join = this.applyAssociationAndStatementsToJoinStatement({
1433
+ join,
1434
+ association: throughAssociation,
1435
+ currentAssociationTableOrAlias,
1436
+ previousAssociationTableOrAlias: originalPreviousAssociationTableOrAlias,
1437
+ joinAndStatements,
1438
+ });
1439
+ });
1440
+ }
1441
+ join = this.conditionallyApplyDefaultScopesDependentOnAssociation({
1442
+ join,
1443
+ tableNameOrAlias: currentAssociationTableOrAlias,
1444
+ association,
1445
+ });
1446
+ join = this.applyJoinAndStatement(join, joinAndStatement, currentAssociationTableOrAlias);
1447
+ return join;
1448
+ });
1449
+ }
1450
+ else {
1451
+ const to = association.modelCB().table;
1452
+ const joinTableExpression = currentAssociationTableOrAlias === to
1453
+ ? currentAssociationTableOrAlias
1454
+ : `${to} as ${currentAssociationTableOrAlias}`;
1455
+ query = query[(joinType === 'inner' ? 'innerJoin' : 'leftJoin')](joinTableExpression, (join) => {
1456
+ join = join.onRef(this.namespaceColumn(association.primaryKey(), previousAssociationTableOrAlias), '=', this.namespaceColumn(association.foreignKey(), currentAssociationTableOrAlias));
1457
+ if (association.polymorphic) {
1458
+ join = join.on((eb) => this.whereStatementToExpressionWrapper(eb, this.aliasWhereStatement({
1459
+ [association.foreignKeyTypeField()]: throughClass
1460
+ ? throughClass['stiBaseClassOrOwnClassName']
1461
+ : dreamClass['stiBaseClassOrOwnClassName'],
1462
+ }, currentAssociationTableOrAlias)));
1463
+ }
1464
+ if (timeToApplyThroughAssociations) {
1465
+ throughAssociations.forEach((throughAssociation) => {
1466
+ join = this.applyAssociationAndStatementsToJoinStatement({
1467
+ join,
1468
+ association: throughAssociation,
1469
+ currentAssociationTableOrAlias,
1470
+ previousAssociationTableOrAlias: originalPreviousAssociationTableOrAlias,
1471
+ joinAndStatements,
1472
+ });
1473
+ });
1474
+ }
1475
+ join = this.applyAssociationAndStatementsToJoinStatement({
1476
+ join,
1477
+ association,
1478
+ currentAssociationTableOrAlias,
1479
+ previousAssociationTableOrAlias,
1480
+ joinAndStatements,
1481
+ });
1482
+ join = this.conditionallyApplyDefaultScopesDependentOnAssociation({
1483
+ join,
1484
+ tableNameOrAlias: currentAssociationTableOrAlias,
1485
+ association,
1486
+ });
1487
+ join = this.applyJoinAndStatement(join, joinAndStatement, currentAssociationTableOrAlias);
1488
+ return join;
1489
+ });
1490
+ if (association.type === 'HasMany') {
1491
+ if (association.order) {
1492
+ query = this.applyOrderStatementForAssociation({
1493
+ query,
1494
+ tableNameOrAlias: currentAssociationTableOrAlias,
1495
+ association,
1496
+ });
1497
+ }
1498
+ if (query.distinctOn && association.distinct) {
1499
+ query = query.distinctOn(this.distinctColumnNameForAssociation({
1500
+ association,
1501
+ tableNameOrAlias: currentAssociationTableOrAlias,
1502
+ foreignKey: association.foreignKey(),
1503
+ }));
1504
+ }
1505
+ }
1506
+ }
1507
+ return {
1508
+ query,
1509
+ association,
1510
+ previousAssociationTableOrAlias,
1511
+ currentAssociationTableOrAlias,
1512
+ };
1513
+ }
1514
+ applyOrderStatementForAssociation({ query, tableNameOrAlias, association, }) {
1515
+ if (!query.orderBy)
1516
+ return query;
1517
+ let selectQuery = query;
1518
+ const orderStatement = association.order;
1519
+ if (typeof orderStatement === 'string') {
1520
+ selectQuery = selectQuery.orderBy(this.namespaceColumn(orderStatement, tableNameOrAlias), 'asc');
1521
+ }
1522
+ else {
1523
+ Object.keys(orderStatement).forEach(column => {
1524
+ const direction = orderStatement[column];
1525
+ selectQuery = selectQuery.orderBy(this.namespaceColumn(column, tableNameOrAlias), direction);
1526
+ });
1527
+ }
1528
+ return selectQuery;
1529
+ }
1530
+ distinctColumnNameForAssociation({ association, tableNameOrAlias, foreignKey, }) {
1531
+ if (!association.distinct)
1532
+ return null;
1533
+ if (association.distinct === true)
1534
+ return this.namespaceColumn(foreignKey, tableNameOrAlias);
1535
+ return this.namespaceColumn(association.distinct, tableNameOrAlias);
1536
+ }
1537
+ applyAssociationAndStatementsToJoinStatement({ join, currentAssociationTableOrAlias, previousAssociationTableOrAlias, association, joinAndStatements, }) {
1538
+ if (association.and) {
1539
+ this.throwUnlessAllRequiredWhereClausesProvided(association, currentAssociationTableOrAlias, joinAndStatements);
1540
+ join = join.on((eb) => this.whereStatementToExpressionWrapper(eb, this.aliasWhereStatement(association.and, currentAssociationTableOrAlias), { disallowSimilarityOperator: false }));
1541
+ }
1542
+ if (association.andNot) {
1543
+ join = join.on((eb) => this.whereStatementToExpressionWrapper(eb, this.aliasWhereStatement(association.andNot, currentAssociationTableOrAlias), { negate: true }));
1544
+ }
1545
+ if (association.andAny) {
1546
+ join = join.on((eb) => eb.or(association.andAny.map(whereAnyStatement => this.whereStatementToExpressionWrapper(eb, this.aliasWhereStatement(whereAnyStatement, currentAssociationTableOrAlias), { disallowSimilarityOperator: false }))));
1547
+ }
1548
+ if (association.selfAnd) {
1549
+ join = join.on((eb) => this.whereStatementToExpressionWrapper(eb, this.rawifiedSelfOnClause({
1550
+ associationAlias: association.as,
1551
+ selfAlias: previousAssociationTableOrAlias,
1552
+ selfAndClause: association.selfAnd,
1553
+ })));
1554
+ }
1555
+ if (association.selfAndNot) {
1556
+ join = join.on((eb) => this.whereStatementToExpressionWrapper(eb, this.rawifiedSelfOnClause({
1557
+ associationAlias: association.as,
1558
+ selfAlias: previousAssociationTableOrAlias,
1559
+ selfAndClause: association.selfAndNot,
1560
+ }), { negate: true }));
1561
+ }
1562
+ return join;
1563
+ }
1564
+ throwUnlessAllRequiredWhereClausesProvided(association, namespace, joinAndStatements) {
1565
+ const andClause = association.and;
1566
+ const columnsRequiringAndStatements = Object.keys(andClause).reduce((agg, column) => {
1567
+ if (andClause[column] === DreamConst.required)
1568
+ agg.push(column);
1569
+ return agg;
1570
+ }, []);
1571
+ const missingRequiredWhereStatements = columnsRequiringAndStatements.filter(column => joinAndStatements[namespace]?.and?.[column] === undefined);
1572
+ if (missingRequiredWhereStatements.length)
1573
+ throw new MissingRequiredAssociationAndClause(association, missingRequiredWhereStatements[0]);
1574
+ }
1575
+ conditionallyApplyDefaultScopesDependentOnAssociation({ join, tableNameOrAlias, association, }) {
1576
+ let scopesQuery = new Query(this.dreamInstance);
1577
+ const associationClass = association.modelCB();
1578
+ const associationScopes = associationClass['scopes'].default;
1579
+ for (const scope of associationScopes) {
1580
+ if (!shouldBypassDefaultScope(scope.method, {
1581
+ bypassAllDefaultScopes: this.query['bypassAllDefaultScopes'],
1582
+ defaultScopesToBypass: [
1583
+ ...this.query['defaultScopesToBypass'],
1584
+ ...(association.withoutDefaultScopes || []),
1585
+ ],
1586
+ })) {
1587
+ const tempQuery = associationClass[scope.method](scopesQuery);
1588
+ // The scope method on a Dream model should return a clone of the Query it receives
1589
+ // (e.g. by returning `scope.where(...)`), but in case the function doesn't return,
1590
+ // or returns the wrong thing, we check before overriding `scopesQuery` with what the
1591
+ // method returned.
1592
+ if (tempQuery && tempQuery.constructor === scopesQuery.constructor)
1593
+ scopesQuery = tempQuery;
1594
+ }
1595
+ }
1596
+ if (scopesQuery['whereStatements'].length) {
1597
+ join = join.on((eb) => eb.and(scopesQuery['whereStatements'].flatMap(whereStatement => this.whereStatementToExpressionWrapper(eb, this.aliasWhereStatement(whereStatement, tableNameOrAlias), { disallowSimilarityOperator: false }))));
1598
+ }
1599
+ return join;
1600
+ }
1601
+ /**
1602
+ * @internal
1603
+ *
1604
+ * Polymorphic BelongsTo. Since polymorphic associations may point to multiple tables,
1605
+ * preload by loading each target class separately.
1606
+ *
1607
+ * Used to preload polymorphic belongs to associations
1608
+ */
1609
+ async preloadPolymorphicBelongsTo(association, dreams) {
1610
+ if (!association.polymorphic)
1611
+ throw new Error(`Association ${association.as} points to an array of models but is not designated polymorphic`);
1612
+ if (association.type !== 'BelongsTo')
1613
+ 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.`);
1614
+ const associatedDreams = [];
1615
+ for (const associatedModel of association.modelCB()) {
1616
+ await this.preloadPolymorphicAssociationModel(dreams, association, associatedModel, associatedDreams);
1617
+ }
1618
+ return associatedDreams;
1619
+ }
1620
+ async preloadPolymorphicAssociationModel(dreams, association, associatedDreamClass, associatedDreams) {
1621
+ const relevantAssociatedModels = dreams.filter((dream) => {
1622
+ const field = association.foreignKeyTypeField();
1623
+ return dream[field] === associatedDreamClass['stiBaseClassOrOwnClassName'] || dream[field] === null;
1624
+ });
1625
+ if (relevantAssociatedModels.length) {
1626
+ dreams.forEach((dream) => {
1627
+ dream[associationToGetterSetterProp(association)] = null;
1628
+ });
1629
+ // Load all models of type associated that are associated with any of the already loaded Dream models
1630
+ const loadedAssociations = await this.dreamClassQueryWithScopeBypasses(associatedDreamClass, {
1631
+ // The association may remove specific default scopes that would otherwise preclude
1632
+ // certain instances of the associated class from being found.
1633
+ defaultScopesToBypassExceptOnAssociations: association.withoutDefaultScopes,
1634
+ })
1635
+ .where({
1636
+ [associatedDreamClass.primaryKey]: relevantAssociatedModels.map((dream) => dream[association.foreignKey()]),
1637
+ })
1638
+ .all();
1639
+ loadedAssociations.forEach((loadedAssociation) => associatedDreams.push(loadedAssociation));
1640
+ //////////////////////////////////////////////////////////////////////////////////////////////
1641
+ // Associate each loaded association with each dream based on primary key and foreign key type
1642
+ //////////////////////////////////////////////////////////////////////////////////////////////
1643
+ for (const loadedAssociation of loadedAssociations) {
1644
+ dreams
1645
+ .filter((dream) => {
1646
+ return (dream[association.foreignKeyTypeField()] === loadedAssociation['stiBaseClassOrOwnClassName'] &&
1647
+ dream[association.foreignKey()] === association.primaryKeyValue(loadedAssociation));
1648
+ })
1649
+ .forEach((dream) => {
1650
+ dream[association.as] = loadedAssociation;
1651
+ });
1652
+ }
1653
+ ///////////////////////////////////////////////////////////////////////////////////////////////////
1654
+ // end: Associate each loaded association with each dream based on primary key and foreign key type
1655
+ ///////////////////////////////////////////////////////////////////////////////////////////////////
1656
+ }
1657
+ }
1658
+ /**
1659
+ * @internal
1660
+ *
1661
+ * Used for applying preload and load statements
1662
+ *
1663
+ * @returns An associated Query
1664
+ */
1665
+ dreamClassQueryWithScopeBypasses(dreamClass, { bypassAllDefaultScopesExceptOnAssociations = false, defaultScopesToBypassExceptOnAssociations = [], } = {}) {
1666
+ const associationQuery = dreamClass.query().clone({
1667
+ passthroughOnStatement: this.query['passthroughOnStatement'],
1668
+ bypassAllDefaultScopes: this.query['bypassAllDefaultScopes'],
1669
+ bypassAllDefaultScopesExceptOnAssociations,
1670
+ defaultScopesToBypass: this.query['defaultScopesToBypass'],
1671
+ defaultScopesToBypassExceptOnAssociations,
1672
+ });
1673
+ return (this.dreamTransaction ? associationQuery.txn(this.dreamTransaction) : associationQuery);
1674
+ }
1675
+ conditionallyAttachSimilarityColumnsToSelect(kyselyQuery, { bypassOrder = false } = {}) {
1676
+ const similarityBuilder = this.similarityStatementBuilder();
1677
+ if (similarityBuilder.hasSimilarityClauses) {
1678
+ kyselyQuery = similarityBuilder.select(kyselyQuery, { bypassOrder });
1679
+ }
1680
+ return kyselyQuery;
1681
+ }
1682
+ conditionallyAttachSimilarityColumnsToUpdate(kyselyQuery) {
1683
+ const similarityBuilder = this.similarityStatementBuilder();
1684
+ if (similarityBuilder.hasSimilarityClauses) {
1685
+ kyselyQuery = similarityBuilder.update(kyselyQuery);
1686
+ }
1687
+ return kyselyQuery;
1688
+ }
1689
+ /**
1690
+ * @internal
1691
+ *
1692
+ * Applies a preload statement
1693
+ */
1694
+ async applyOnePreload(associationName, dreams, onStatement = {}) {
1695
+ if (!Array.isArray(dreams))
1696
+ dreams = [dreams];
1697
+ const dream = dreams.find(dream => dream['getAssociationMetadata'](associationName));
1698
+ if (!dream)
1699
+ return;
1700
+ const { name, alias } = extractAssociationMetadataFromAssociationName(associationName);
1701
+ const association = dream['getAssociationMetadata'](name);
1702
+ if (association === undefined)
1703
+ throw new UnexpectedUndefined();
1704
+ const dreamClass = dream.constructor;
1705
+ const dreamClassToHydrate = association.modelCB();
1706
+ if ((association.polymorphic && association.type === 'BelongsTo') || Array.isArray(dreamClassToHydrate))
1707
+ return this.preloadPolymorphicBelongsTo(association, dreams);
1708
+ const dreamClassToHydrateColumns = [...dreamClassToHydrate.columns()];
1709
+ const throughColumnsToHydrate = [];
1710
+ const columnsToPluck = dreamClassToHydrateColumns.map(column => this.namespaceColumn(column.toString(), alias));
1711
+ const asHasAssociation = association;
1712
+ if (asHasAssociation.through && asHasAssociation.preloadThroughColumns) {
1713
+ if (isObject(asHasAssociation.preloadThroughColumns)) {
1714
+ const preloadMap = asHasAssociation.preloadThroughColumns;
1715
+ Object.keys(preloadMap).forEach(preloadThroughColumn => {
1716
+ throughColumnsToHydrate.push(preloadMap[preloadThroughColumn]);
1717
+ columnsToPluck.push(this.namespaceColumn(preloadThroughColumn, asHasAssociation.through));
1718
+ });
1719
+ }
1720
+ else {
1721
+ const preloadArray = asHasAssociation.preloadThroughColumns;
1722
+ preloadArray.forEach(preloadThroughColumn => {
1723
+ throughColumnsToHydrate.push(preloadThroughColumn);
1724
+ columnsToPluck.push(this.namespaceColumn(preloadThroughColumn, asHasAssociation.through));
1725
+ });
1726
+ }
1727
+ }
1728
+ columnsToPluck.push(this.namespaceColumn(dreamClass.primaryKey, dreamClass.table));
1729
+ const baseClass = dreamClass['stiBaseClassOrOwnClass']['getAssociationMetadata'](associationName)
1730
+ ? dreamClass['stiBaseClassOrOwnClass']
1731
+ : dreamClass;
1732
+ const associationDataScope = this.dreamClassQueryWithScopeBypasses(baseClass, {
1733
+ // In order to stay DRY, preloading leverages the association logic built into
1734
+ // `joins` (by using `pluck`, which calls `joins`). However, baseClass may have
1735
+ // default scopes that would preclude finding that instance. We remove all
1736
+ // default scopes on baseClass, but not subsequent associations, so that the
1737
+ // single query will be able to find each row corresponding to a Dream in `dreams`,
1738
+ // regardless of default scopes on that Dream's class.
1739
+ bypassAllDefaultScopesExceptOnAssociations: true,
1740
+ }).where({
1741
+ [dreamClass.primaryKey]: dreams.map(obj => obj.primaryKeyValue),
1742
+ });
1743
+ const hydrationData = await associationDataScope['_connection'](this.connectionOverride)
1744
+ .innerJoin(associationName, (onStatement || {}))
1745
+ .pluck(...columnsToPluck);
1746
+ const preloadedDreamsAndWhatTheyPointTo = hydrationData.map(pluckedData => {
1747
+ const attributes = {};
1748
+ dreamClassToHydrateColumns.forEach((columnName, index) => (attributes[protectAgainstPollutingAssignment(columnName)] = pluckedData[index]));
1749
+ const hydratedDream = this.dbResultToDreamInstance(attributes, dreamClassToHydrate);
1750
+ throughColumnsToHydrate.forEach((throughAssociationColumn, index) => (hydratedDream.preloadedThroughColumns[throughAssociationColumn] =
1751
+ pluckedData[dreamClassToHydrateColumns.length + index]));
1752
+ return {
1753
+ dream: hydratedDream,
1754
+ pointsToPrimaryKey: pluckedData.at(-1),
1755
+ };
1756
+ });
1757
+ this.hydrateAssociation(dreams, association, preloadedDreamsAndWhatTheyPointTo);
1758
+ return preloadedDreamsAndWhatTheyPointTo.map(obj => obj.dream);
1759
+ }
1760
+ }
1761
+ function getSourceAssociation(dream, sourceName) {
1762
+ if (!dream)
1763
+ return;
1764
+ if (!sourceName)
1765
+ return;
1766
+ return (dream['getAssociationMetadata'](sourceName) ||
1767
+ dream['getAssociationMetadata'](pluralize.singular(sourceName)));
1768
+ }