objection 3.9.0 → 4.0.0-rc.1

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 (248) hide show
  1. package/lib/dbErrors/LICENSE +21 -0
  2. package/lib/dbErrors/errorCodes/mssql.js +19 -0
  3. package/lib/dbErrors/errorCodes/mysql.js +805 -0
  4. package/lib/dbErrors/errorCodes/postgres.js +241 -0
  5. package/lib/dbErrors/errors/CheckViolationError.js +10 -0
  6. package/lib/dbErrors/errors/ConstraintViolationError.js +3 -0
  7. package/lib/dbErrors/errors/DBError.js +9 -0
  8. package/lib/dbErrors/errors/DataError.js +3 -0
  9. package/lib/dbErrors/errors/ForeignKeyViolationError.js +11 -0
  10. package/lib/dbErrors/errors/NotNullViolationError.js +12 -0
  11. package/lib/dbErrors/errors/UniqueViolationError.js +12 -0
  12. package/lib/dbErrors/index.js +59 -0
  13. package/lib/dbErrors/parsers/mssql/DBError/CheckViolationError/parser.js +28 -0
  14. package/lib/dbErrors/parsers/mssql/DBError/DataError/parser.js +25 -0
  15. package/lib/dbErrors/parsers/mssql/DBError/ForeignKeyViolationError/parser.js +39 -0
  16. package/lib/dbErrors/parsers/mssql/DBError/NotNullViolationError/parser.js +30 -0
  17. package/lib/dbErrors/parsers/mssql/DBError/UniqueViolationError/parser.js +53 -0
  18. package/lib/dbErrors/parsers/mssql/DBError/parser.js +51 -0
  19. package/lib/dbErrors/parsers/mssql/DBError/util.js +16 -0
  20. package/lib/dbErrors/parsers/mysql/DBError/ConstraintViolationError/ForeignKeyViolationError/parser.js +43 -0
  21. package/lib/dbErrors/parsers/mysql/DBError/ConstraintViolationError/NotNullViolationError/parser.js +29 -0
  22. package/lib/dbErrors/parsers/mysql/DBError/ConstraintViolationError/UniqueViolationError/parser.js +27 -0
  23. package/lib/dbErrors/parsers/mysql/DBError/ConstraintViolationError/parser.js +23 -0
  24. package/lib/dbErrors/parsers/mysql/DBError/DataError/parser.js +24 -0
  25. package/lib/dbErrors/parsers/mysql/DBError/parser.js +28 -0
  26. package/lib/dbErrors/parsers/postgres/DBError/ConstraintViolationError/CheckViolationError/parser.js +18 -0
  27. package/lib/dbErrors/parsers/postgres/DBError/ConstraintViolationError/ForeignKeyViolationError/parser.js +18 -0
  28. package/lib/dbErrors/parsers/postgres/DBError/ConstraintViolationError/NotNullViolationError/parser.js +18 -0
  29. package/lib/dbErrors/parsers/postgres/DBError/ConstraintViolationError/UniqueViolationError/parser.js +40 -0
  30. package/lib/dbErrors/parsers/postgres/DBError/ConstraintViolationError/parser.js +25 -0
  31. package/lib/dbErrors/parsers/postgres/DBError/DataError/parser.js +16 -0
  32. package/lib/dbErrors/parsers/postgres/DBError/parser.js +27 -0
  33. package/lib/dbErrors/parsers/sqlite/DBError/ConstraintViolationError/CheckViolationError/parser.js +20 -0
  34. package/lib/dbErrors/parsers/sqlite/DBError/ConstraintViolationError/ForeignKeyViolationError/parser.js +20 -0
  35. package/lib/dbErrors/parsers/sqlite/DBError/ConstraintViolationError/NotNullViolationError/parser.js +27 -0
  36. package/lib/dbErrors/parsers/sqlite/DBError/ConstraintViolationError/UniqueViolationError/parser.js +28 -0
  37. package/lib/dbErrors/parsers/sqlite/DBError/ConstraintViolationError/parser.js +24 -0
  38. package/lib/dbErrors/parsers/sqlite/DBError/parser.js +23 -0
  39. package/lib/dbErrors/utils/sqlState.js +3 -0
  40. package/lib/initialize.js +1 -7
  41. package/lib/model/AjvValidator.js +51 -117
  42. package/lib/model/Model.js +84 -59
  43. package/lib/model/ModifierNotFoundError.js +1 -7
  44. package/lib/model/NotFoundError.js +1 -7
  45. package/lib/model/RelationDoesNotExistError.js +1 -7
  46. package/lib/model/ValidationError.js +3 -10
  47. package/lib/model/Validator.js +1 -7
  48. package/lib/model/getModel.js +7 -6
  49. package/lib/model/inheritModel.js +1 -7
  50. package/lib/model/modelBindKnex.js +4 -10
  51. package/lib/model/modelClone.js +5 -11
  52. package/lib/model/modelColPropMap.js +3 -10
  53. package/lib/model/modelId.js +3 -10
  54. package/lib/model/modelJsonAttributes.js +35 -16
  55. package/lib/model/modelParseRelations.js +2 -8
  56. package/lib/model/modelQueryProps.js +43 -27
  57. package/lib/model/modelSet.js +89 -41
  58. package/lib/model/modelTableMetadata.js +4 -11
  59. package/lib/model/modelToJson.js +8 -23
  60. package/lib/model/modelUtils.js +14 -13
  61. package/lib/model/modelValidate.js +3 -9
  62. package/lib/model/modelValues.js +5 -14
  63. package/lib/model/modelVisitor.js +1 -7
  64. package/lib/objection.js +33 -71
  65. package/lib/queryBuilder/FunctionBuilder.js +4 -11
  66. package/lib/queryBuilder/InternalOptions.js +4 -9
  67. package/lib/queryBuilder/JoinBuilder.js +5 -8
  68. package/lib/queryBuilder/QueryBuilder.js +126 -95
  69. package/lib/queryBuilder/QueryBuilderBase.js +41 -123
  70. package/lib/queryBuilder/QueryBuilderContext.js +2 -8
  71. package/lib/queryBuilder/QueryBuilderContextBase.js +2 -8
  72. package/lib/queryBuilder/QueryBuilderOperationSupport.js +7 -21
  73. package/lib/queryBuilder/QueryBuilderUserContext.js +1 -7
  74. package/lib/queryBuilder/RawBuilder.js +5 -13
  75. package/lib/queryBuilder/ReferenceBuilder.js +20 -48
  76. package/lib/queryBuilder/RelationExpression.js +18 -16
  77. package/lib/queryBuilder/StaticHookArguments.js +74 -16
  78. package/lib/queryBuilder/ValueBuilder.js +11 -13
  79. package/lib/queryBuilder/getJoinBuilder.js +10 -0
  80. package/lib/queryBuilder/graph/GraphLoader.js +304 -0
  81. package/lib/queryBuilder/graph/GraphNode.js +235 -0
  82. package/lib/queryBuilder/graph/GraphOptions.js +55 -248
  83. package/lib/queryBuilder/graph/GraphPlan.js +395 -0
  84. package/lib/queryBuilder/graph/GraphUpsert.js +466 -410
  85. package/lib/queryBuilder/graph/GraphWriter.js +1263 -0
  86. package/lib/queryBuilder/join/JoinResultColumn.js +1 -7
  87. package/lib/queryBuilder/join/JoinResultParser.js +6 -11
  88. package/lib/queryBuilder/join/RelationJoiner.js +66 -84
  89. package/lib/queryBuilder/join/TableNode.js +2 -8
  90. package/lib/queryBuilder/join/TableTree.js +3 -9
  91. package/lib/queryBuilder/join/utils.js +5 -15
  92. package/lib/queryBuilder/jsonDialects/JsonDialect.js +51 -0
  93. package/lib/queryBuilder/jsonDialects/MySqlJsonDialect.js +81 -0
  94. package/lib/queryBuilder/jsonDialects/PostgresJsonDialect.js +45 -0
  95. package/lib/queryBuilder/jsonDialects/SqliteJsonDialect.js +67 -0
  96. package/lib/queryBuilder/jsonDialects/index.js +44 -0
  97. package/lib/queryBuilder/operations/DelegateOperation.js +2 -8
  98. package/lib/queryBuilder/operations/DeleteOperation.js +13 -35
  99. package/lib/queryBuilder/operations/FindByIdOperation.js +7 -12
  100. package/lib/queryBuilder/operations/FindByIdsOperation.js +4 -8
  101. package/lib/queryBuilder/operations/FindOperation.js +14 -34
  102. package/lib/queryBuilder/operations/FirstOperation.js +6 -10
  103. package/lib/queryBuilder/operations/FromOperation.js +3 -9
  104. package/lib/queryBuilder/operations/InsertAndFetchOperation.js +13 -12
  105. package/lib/queryBuilder/operations/InsertGraphAndFetchOperation.js +4 -10
  106. package/lib/queryBuilder/operations/InsertGraphOperation.js +7 -18
  107. package/lib/queryBuilder/operations/InsertOperation.js +118 -43
  108. package/lib/queryBuilder/operations/InstanceDeleteOperation.js +5 -12
  109. package/lib/queryBuilder/operations/InstanceFindOperation.js +4 -11
  110. package/lib/queryBuilder/operations/InstanceInsertOperation.js +2 -8
  111. package/lib/queryBuilder/operations/InstanceUpdateOperation.js +7 -14
  112. package/lib/queryBuilder/operations/JoinRelatedOperation.js +5 -11
  113. package/lib/queryBuilder/operations/KnexOperation.js +2 -8
  114. package/lib/queryBuilder/operations/LimitPerParentOperation.js +7 -69
  115. package/lib/queryBuilder/operations/MergeOperation.js +5 -11
  116. package/lib/queryBuilder/operations/NoneOperation.js +2 -8
  117. package/lib/queryBuilder/operations/ObjectionToKnexConvertingOperation.js +20 -49
  118. package/lib/queryBuilder/operations/OnBuildKnexOperation.js +2 -8
  119. package/lib/queryBuilder/operations/OnBuildOperation.js +2 -8
  120. package/lib/queryBuilder/operations/OnErrorOperation.js +2 -8
  121. package/lib/queryBuilder/operations/QueryBuilderOperation.js +1 -7
  122. package/lib/queryBuilder/operations/RangeOperation.js +11 -26
  123. package/lib/queryBuilder/operations/RelateOperation.js +2 -8
  124. package/lib/queryBuilder/operations/ReturningOperation.js +3 -9
  125. package/lib/queryBuilder/operations/RunAfterOperation.js +2 -12
  126. package/lib/queryBuilder/operations/RunBeforeOperation.js +2 -8
  127. package/lib/queryBuilder/operations/UnrelateOperation.js +2 -8
  128. package/lib/queryBuilder/operations/UpdateAndFetchOperation.js +14 -17
  129. package/lib/queryBuilder/operations/UpdateOperation.js +39 -125
  130. package/lib/queryBuilder/operations/UpsertGraphAndFetchOperation.js +8 -11
  131. package/lib/queryBuilder/operations/UpsertGraphOperation.js +6 -12
  132. package/lib/queryBuilder/operations/WhereCompositeOperation.js +3 -9
  133. package/lib/queryBuilder/operations/WhereGroupOperation.js +2 -8
  134. package/lib/queryBuilder/operations/eager/EagerOperation.js +3 -9
  135. package/lib/queryBuilder/operations/eager/JoinEagerOperation.js +72 -10
  136. package/lib/queryBuilder/operations/eager/NaiveEagerOperation.js +2 -8
  137. package/lib/queryBuilder/operations/eager/WhereInEagerOperation.js +23 -44
  138. package/lib/queryBuilder/operations/fieldExpressions.js +122 -0
  139. package/lib/queryBuilder/operations/jsonApi/WhereJsonHasOperation.js +20 -0
  140. package/lib/queryBuilder/operations/jsonApi/WhereJsonOperation.js +44 -0
  141. package/lib/queryBuilder/operations/jsonApi/WhereJsonTypeOperation.js +14 -0
  142. package/lib/queryBuilder/operations/select/SelectOperation.js +11 -21
  143. package/lib/queryBuilder/operations/select/Selection.js +19 -17
  144. package/lib/queryBuilder/operations/whereInComposite/WhereInCompositeMsSqlOperation.js +4 -10
  145. package/lib/queryBuilder/operations/whereInComposite/WhereInCompositeOperation.js +4 -10
  146. package/lib/queryBuilder/operations/whereInComposite/WhereInCompositeSqliteOperation.js +4 -10
  147. package/lib/queryBuilder/operations/windowOrder.js +56 -0
  148. package/lib/queryBuilder/parsers/jsonFieldExpressionParser.js +3 -7
  149. package/lib/queryBuilder/parsers/relationExpressionParser.js +42 -43
  150. package/lib/queryBuilder/parsers/relationExpressionParser.pegjs +39 -36
  151. package/lib/queryBuilder/transformations/CompositeQueryTransformation.js +2 -8
  152. package/lib/queryBuilder/transformations/QueryTransformation.js +1 -7
  153. package/lib/queryBuilder/transformations/WrapMysqlModifySubqueryTransformation.js +3 -9
  154. package/lib/queryBuilder/transformations/index.js +3 -11
  155. package/lib/queryBuilder/whereGroups.js +9 -24
  156. package/lib/queryBuilder/withoutOrder.js +4 -11
  157. package/lib/relations/Relation.js +47 -28
  158. package/lib/relations/RelationDeleteOperation.js +4 -14
  159. package/lib/relations/RelationFindOperation.js +119 -90
  160. package/lib/relations/RelationInsertOperation.js +2 -8
  161. package/lib/relations/RelationOwner.js +5 -11
  162. package/lib/relations/RelationProperty.js +4 -10
  163. package/lib/relations/RelationUpdateOperation.js +4 -14
  164. package/lib/relations/RelationWriteResultOperation.js +15 -0
  165. package/lib/relations/belongsToMany/BelongsToManyDeleteOperation.js +23 -0
  166. package/lib/relations/belongsToMany/BelongsToManyFindOperation.js +35 -0
  167. package/lib/relations/belongsToMany/BelongsToManyInsertOperation.js +28 -0
  168. package/lib/relations/belongsToMany/BelongsToManyRelateOperation.js +16 -0
  169. package/lib/relations/belongsToMany/BelongsToManyRelation.js +276 -0
  170. package/lib/relations/belongsToMany/BelongsToManyUnrelateOperation.js +11 -0
  171. package/lib/relations/belongsToOne/BelongsToOneDeleteOperation.js +2 -8
  172. package/lib/relations/belongsToOne/BelongsToOneInsertOperation.js +6 -11
  173. package/lib/relations/belongsToOne/BelongsToOneRelateOperation.js +3 -9
  174. package/lib/relations/belongsToOne/BelongsToOneRelation.js +8 -19
  175. package/lib/relations/belongsToOne/BelongsToOneUnrelateOperation.js +53 -27
  176. package/lib/relations/hasMany/HasManyInsertOperation.js +8 -9
  177. package/lib/relations/hasMany/HasManyRelateOperation.js +5 -11
  178. package/lib/relations/hasMany/HasManyRelation.js +9 -11
  179. package/lib/relations/hasMany/HasManyUnrelateOperation.js +3 -9
  180. package/lib/relations/hasOne/HasOneRelation.js +2 -8
  181. package/lib/relations/hasOneThrough/HasOneThroughRelation.js +2 -8
  182. package/lib/relations/manyToMany/ManyToManyModifyMixin.js +10 -15
  183. package/lib/relations/manyToMany/ManyToManyMySqlModifyMixin.js +2 -8
  184. package/lib/relations/manyToMany/ManyToManyRelation.js +82 -50
  185. package/lib/relations/manyToMany/ManyToManySqliteModifyMixin.js +2 -8
  186. package/lib/relations/manyToMany/delete/ManyToManyDeleteOperation.js +5 -9
  187. package/lib/relations/manyToMany/delete/ManyToManyDeleteOperationBase.js +2 -8
  188. package/lib/relations/manyToMany/delete/ManyToManyDeleteSqliteOperation.js +3 -9
  189. package/lib/relations/manyToMany/find/ManyToManyFindOperation.js +20 -59
  190. package/lib/relations/manyToMany/insert/ManyToManyInsertOperation.js +6 -11
  191. package/lib/relations/manyToMany/relate/ManyToManyRelateOperation.js +4 -10
  192. package/lib/relations/manyToMany/unrelate/ManyToManyUnrelateMySqlOperation.js +3 -9
  193. package/lib/relations/manyToMany/unrelate/ManyToManyUnrelateOperation.js +5 -9
  194. package/lib/relations/manyToMany/unrelate/ManyToManyUnrelateOperationBase.js +3 -9
  195. package/lib/relations/manyToMany/unrelate/ManyToManyUnrelateSqliteOperation.js +3 -9
  196. package/lib/relations/manyToMany/update/ManyToManyUpdateMySqlOperation.js +3 -9
  197. package/lib/relations/manyToMany/update/ManyToManyUpdateOperation.js +5 -9
  198. package/lib/relations/manyToMany/update/ManyToManyUpdateOperationBase.js +79 -64
  199. package/lib/relations/manyToMany/update/ManyToManyUpdateSqliteOperation.js +3 -9
  200. package/lib/transaction.js +3 -9
  201. package/lib/utils/assert.js +3 -11
  202. package/lib/utils/buildUtils.js +2 -8
  203. package/lib/utils/classUtils.js +1 -7
  204. package/lib/utils/createModifier.js +2 -8
  205. package/lib/utils/identifierMapping.js +23 -27
  206. package/lib/utils/internalPropUtils.js +1 -7
  207. package/lib/utils/knexUtils.js +84 -40
  208. package/lib/utils/mixin.js +3 -10
  209. package/lib/utils/normalizeIds.js +2 -8
  210. package/lib/utils/objectUtils.js +41 -60
  211. package/lib/utils/parseFieldExpression.js +29 -14
  212. package/lib/utils/promiseUtils/after.js +2 -8
  213. package/lib/utils/promiseUtils/afterReturn.js +2 -8
  214. package/lib/utils/promiseUtils/index.js +7 -16
  215. package/lib/utils/promiseUtils/isPromise.js +2 -8
  216. package/lib/utils/promiseUtils/map.js +25 -14
  217. package/lib/utils/promiseUtils/mapAfterAllReturn.js +2 -8
  218. package/lib/utils/promiseUtils/try.js +2 -8
  219. package/lib/utils/resolveModel.js +10 -17
  220. package/lib/utils/tmpColumnUtils.js +2 -9
  221. package/lib/utils/warnings.js +2 -14
  222. package/package.json +25 -17
  223. package/typings/objection/index.d.ts +3217 -2179
  224. package/lib/.eslintrc.json +0 -8
  225. package/lib/model/graph/ModelGraph.js +0 -80
  226. package/lib/model/graph/ModelGraphBuilder.js +0 -241
  227. package/lib/model/graph/ModelGraphEdge.js +0 -52
  228. package/lib/model/graph/ModelGraphNode.js +0 -253
  229. package/lib/queryBuilder/graph/GraphAction.js +0 -69
  230. package/lib/queryBuilder/graph/GraphData.js +0 -53
  231. package/lib/queryBuilder/graph/GraphFetcher.js +0 -199
  232. package/lib/queryBuilder/graph/GraphNodeDbExistence.js +0 -184
  233. package/lib/queryBuilder/graph/GraphOperation.js +0 -27
  234. package/lib/queryBuilder/graph/delete/GraphDelete.js +0 -73
  235. package/lib/queryBuilder/graph/delete/GraphDeleteAction.js +0 -88
  236. package/lib/queryBuilder/graph/insert/DeferredRelationGraphInsertAction.js +0 -57
  237. package/lib/queryBuilder/graph/insert/GraphInsert.js +0 -257
  238. package/lib/queryBuilder/graph/insert/GraphInsertAction.js +0 -238
  239. package/lib/queryBuilder/graph/insert/JoinRowGraphInsertAction.js +0 -92
  240. package/lib/queryBuilder/graph/patch/GraphPatch.js +0 -20
  241. package/lib/queryBuilder/graph/patch/GraphPatchAction.js +0 -277
  242. package/lib/queryBuilder/graph/recursiveUpsert/GraphRecursiveUpsert.js +0 -31
  243. package/lib/queryBuilder/graph/recursiveUpsert/GraphRecursiveUpsertAction.js +0 -126
  244. package/lib/queryBuilder/operations/jsonApi/WhereJsonHasPostgresOperation.js +0 -30
  245. package/lib/queryBuilder/operations/jsonApi/WhereJsonNotObjectPostgresOperation.js +0 -37
  246. package/lib/queryBuilder/operations/jsonApi/WhereJsonPostgresOperation.js +0 -62
  247. package/lib/queryBuilder/operations/jsonApi/postgresJsonApi.js +0 -192
  248. package/lib/utils/wrapDbError.js +0 -50
@@ -1,11 +1,9 @@
1
- 'use strict';
2
-
3
- const { asArray } = require('../utils/objectUtils');
1
+ import { asArray, isObject } from '../utils/objectUtils.js';
4
2
 
5
3
  const BUILDER_SYMBOL = Symbol();
6
4
 
7
- class StaticHookArguments {
8
- constructor({ builder, result = null }) {
5
+ export class StaticHookArguments {
6
+ constructor({ builder, result }) {
9
7
  // The builder should never be accessed through the arguments.
10
8
  // Hide it as well as possible to discourage people from
11
9
  // digging it out.
@@ -14,7 +12,7 @@ class StaticHookArguments {
14
12
  });
15
13
 
16
14
  Object.defineProperty(this, 'result', {
17
- value: asArray(result),
15
+ value: result,
18
16
  });
19
17
  }
20
18
 
@@ -33,7 +31,7 @@ class StaticHookArguments {
33
31
  .clear('runAfter')
34
32
  .clearResolve()
35
33
  .clearWithGraphFetched()
36
- .runAfter(toArray);
34
+ .runAfter(asArray);
37
35
  };
38
36
  }
39
37
 
@@ -46,7 +44,7 @@ class StaticHookArguments {
46
44
  }
47
45
 
48
46
  get relation() {
49
- const op = this[BUILDER_SYMBOL].findOperation(hasRelation);
47
+ const op = findRelationOperation(this[BUILDER_SYMBOL], hasRelation);
50
48
 
51
49
  if (op) {
52
50
  return getRelation(op);
@@ -66,7 +64,7 @@ class StaticHookArguments {
66
64
  }
67
65
 
68
66
  get items() {
69
- const op = this[BUILDER_SYMBOL].findOperation(hasItems);
67
+ const op = findRelationOperation(this[BUILDER_SYMBOL], hasItems);
70
68
 
71
69
  if (op) {
72
70
  return asArray(getItems(op));
@@ -99,6 +97,9 @@ class StaticHookArguments {
99
97
  } else {
100
98
  cancelValue = 0;
101
99
  }
100
+ } else if (builder.isInsert() && isObject(cancelValue)) {
101
+ // A single row is merged into the input model, like an array of rows.
102
+ cancelValue = asArray(cancelValue);
102
103
  }
103
104
 
104
105
  builder.resolve(cancelValue);
@@ -106,9 +107,70 @@ class StaticHookArguments {
106
107
  }
107
108
  }
108
109
 
109
- // Like `asArray()`, but an empty `first()` result becomes an empty array.
110
- function toArray(result) {
111
- return result === undefined ? [] : asArray(result);
110
+ // The outer queries of `insertGraph()` and `upsertGraph()` call no static hooks,
111
+ // the queries that they run for the graph do. Match their operations by name,
112
+ // as importing them here would create an import cycle.
113
+ export function isGraphQuery(builder) {
114
+ return builder.has(/^(insertGraph|upsertGraph)/);
115
+ }
116
+
117
+ // The relations that `withGraphJoined()` fetches have no query of their own, so
118
+ // the hooks of their model classes get the relation and its owners instead.
119
+ export class JoinedStaticHookArguments extends StaticHookArguments {
120
+ #relation;
121
+ #items;
122
+
123
+ constructor({ builder, result, relation, items }) {
124
+ super({ builder, result });
125
+ this.#relation = relation;
126
+ this.#items = items;
127
+ }
128
+
129
+ static create(args) {
130
+ return new JoinedStaticHookArguments(args);
131
+ }
132
+
133
+ get asFindQuery() {
134
+ return () => {
135
+ throw new Error(
136
+ 'asFindQuery() is not available for relations fetched with withGraphJoined().',
137
+ );
138
+ };
139
+ }
140
+
141
+ get relation() {
142
+ return this.#relation;
143
+ }
144
+
145
+ get modelOptions() {
146
+ return undefined;
147
+ }
148
+
149
+ get items() {
150
+ return this.#items;
151
+ }
152
+
153
+ get inputItems() {
154
+ return [];
155
+ }
156
+
157
+ get cancelQuery() {
158
+ return () => {
159
+ throw new Error(
160
+ 'cancelQuery() is not available for relations fetched with withGraphJoined().',
161
+ );
162
+ };
163
+ }
164
+ }
165
+
166
+ // The queries that `relate()` and `unrelate()` run in their place get the
167
+ // relation and the owners of the relate or unrelate query.
168
+ function findRelationOperation(builder, predicate) {
169
+ const parent = builder.parentQuery();
170
+ return (
171
+ builder.findOperation(predicate) ||
172
+ (parent?.isRelate() || parent?.isUnrelate() ? parent.findOperation(predicate) : null)
173
+ );
112
174
  }
113
175
 
114
176
  function getRelation(op) {
@@ -142,7 +204,3 @@ function getInputItems(op) {
142
204
  function hasInputItems(op) {
143
205
  return !!getInputItems(op);
144
206
  }
145
-
146
- module.exports = {
147
- StaticHookArguments,
148
- };
@@ -1,9 +1,8 @@
1
- 'use strict';
1
+ import { isObject } from '../utils/objectUtils.js';
2
+ import { buildArg } from '../utils/buildUtils.js';
3
+ import { getJsonDialect } from './jsonDialects/index.js';
2
4
 
3
- const { asArray, isObject } = require('../utils/objectUtils');
4
- const { buildArg } = require('../utils/buildUtils');
5
-
6
- class ValueBuilder {
5
+ export class ValueBuilder {
7
6
  constructor(value) {
8
7
  this._value = value;
9
8
  this._cast = null;
@@ -73,6 +72,7 @@ class ValueBuilder {
73
72
  }
74
73
 
75
74
  _createRawArgs(builder) {
75
+ const dialect = getJsonDialect(builder.knex());
76
76
  let sql = null;
77
77
  let bindings = [];
78
78
 
@@ -80,7 +80,8 @@ class ValueBuilder {
80
80
  bindings.push(JSON.stringify(this._value));
81
81
  sql = '?';
82
82
  } else if (this._toArray) {
83
- const values = asArray(this._value);
83
+ // Wrap `undefined` too, it is a value here:
84
+ const values = Array.isArray(this._value) ? this._value : [this._value];
84
85
  bindings.push(...values.map((it) => buildArg(it, builder)));
85
86
  sql = `ARRAY[${values.map(() => '?').join(', ')}]`;
86
87
  } else {
@@ -89,7 +90,9 @@ class ValueBuilder {
89
90
  }
90
91
 
91
92
  if (this._cast) {
92
- sql = `CAST(${sql} AS ${this._cast})`;
93
+ sql = dialect.cast(sql, this._cast);
94
+ } else if (this._toJson) {
95
+ sql = dialect.json(sql);
93
96
  }
94
97
 
95
98
  if (this._alias) {
@@ -101,11 +104,6 @@ class ValueBuilder {
101
104
  }
102
105
  }
103
106
 
104
- function val(val) {
107
+ export function val(val) {
105
108
  return new ValueBuilder(val);
106
109
  }
107
-
108
- module.exports = {
109
- ValueBuilder,
110
- val,
111
- };
@@ -0,0 +1,10 @@
1
+ // A small helper for lazy access to the JoinBuilder class, for modules that the
2
+ // JoinBuilder module imports itself. Importing JoinBuilder.js there would create
3
+ // a circular import, so JoinBuilder.js registers itself here instead.
4
+ let JoinBuilder;
5
+
6
+ export const getJoinBuilder = () => JoinBuilder;
7
+
8
+ export const setJoinBuilder = (joinBuilder) => {
9
+ JoinBuilder = joinBuilder;
10
+ };
@@ -0,0 +1,304 @@
1
+ import { asArray, chunk, groupBy, uniqBy } from '../../utils/objectUtils.js';
2
+ import { getMaxRows } from '../../utils/knexUtils.js';
3
+ import { FetchStrategy } from './GraphOptions.js';
4
+ import { GraphNode } from './GraphNode.js';
5
+ import { RelationExpression } from '../RelationExpression.js';
6
+ import { getDeclaredColumns } from '../operations/fieldExpressions.js';
7
+ import { getFieldExpressionColumn } from '../../utils/parseFieldExpression.js';
8
+
9
+ // Fetch the current state of the graph from the database and pair its rows with
10
+ // the models of the graph: First the roots of the pass with their relations,
11
+ // then the rows of the models that the graph relates from elsewhere, with the
12
+ // relations that the graph gives them, level by level, as the rows of related
13
+ // models decide which models below them are related too. Models that
14
+ // `insertMissing` inserts if they don't exist are looked up too. The rows that
15
+ // a relate moves into a HasManyRelation or HasOneRelation are fetched without
16
+ // their relations, which change with the move. Return the ones that the graph
17
+ // gives relations, to be upserted in a pass of their own after the move.
18
+ export async function loadCurrentGraph(builder, plan) {
19
+ const passNodes = new Set(plan.nodes);
20
+ const fetchedNodes = new Set();
21
+ const movedNodes = [];
22
+
23
+ const fetch = async (nodes, { areRelated }) => {
24
+ for (const node of nodes) {
25
+ fetchedNodes.add(node);
26
+ }
27
+
28
+ for (const [isMovedRow, groupNodes] of groupBy(nodes, (node) => areRelated && isMoved(node))) {
29
+ for (const nodesOfClass of groupBy(groupNodes, (node) => node.modelClass).values()) {
30
+ const models = await fetchModels(builder, plan.graphOptions, nodesOfClass, {
31
+ withRelations: !isMovedRow,
32
+ });
33
+ const modelsByKey = new Map(models.map((model) => [model.$idKey(), model]));
34
+
35
+ for (const node of nodesOfClass) {
36
+ const model = modelsByKey.get(node.obj.$idKey());
37
+
38
+ if (model && isMovedRow && hasRelations(node.obj)) {
39
+ movedNodes.push(node);
40
+ } else if (model) {
41
+ const currentNodes = GraphNode.buildGraph(node.modelClass, model, node.relationPath);
42
+ const [currentNode] = currentNodes;
43
+ plan.currentNodes.push(...currentNodes);
44
+
45
+ if (areRelated) {
46
+ node.row = currentNode;
47
+ currentNode.node = node;
48
+ pairChildren(node, currentNode);
49
+ } else {
50
+ pair(node, currentNode);
51
+ }
52
+ } else if (areRelated) {
53
+ node.isMissing = true;
54
+ }
55
+ }
56
+ }
57
+ }
58
+ };
59
+
60
+ // Find the models that the graph relates and that aren't fetched yet, for
61
+ // which `isFetched(node, rowKeys)` is true. The ones below others wait for
62
+ // their rows, and the ones in branches that the graph doesn't write are left
63
+ // out.
64
+ const findRelatedNodes = (isFetched) => {
65
+ const rowKeys = new Set(plan.currentNodes.map((currentNode) => currentNode.rowKey));
66
+ const nodes = plan.nodes.filter(
67
+ (node) =>
68
+ !fetchedNodes.has(node) &&
69
+ !node.current &&
70
+ node.obj.$hasId() &&
71
+ plan.actionFor(node) === 'relate' &&
72
+ isFetched(node, rowKeys),
73
+ );
74
+ const nodeSet = new Set(nodes);
75
+
76
+ return nodes.filter((node) => {
77
+ for (let parent = node.parent; passNodes.has(parent); parent = parent.parent) {
78
+ if (nodeSet.has(parent) || !plan.writesBranchOf(parent)) {
79
+ return false;
80
+ }
81
+ }
82
+
83
+ return true;
84
+ });
85
+ };
86
+
87
+ await fetch(
88
+ plan.nodes.filter((node) => !passNodes.has(node.parent) && node.hasId),
89
+ { areRelated: false },
90
+ );
91
+
92
+ // The rows of the related models that the graph gives relations, or inserts
93
+ // with `insertMissing` if they don't exist:
94
+ const hasRelatedRow = (node, rowKeys) =>
95
+ hasRelations(node.obj) ||
96
+ (plan.graphOptions.has(node, 'insertMissing') && !rowKeys.has(node.rowKey));
97
+
98
+ for (
99
+ let nodes = findRelatedNodes(hasRelatedRow);
100
+ nodes.length > 0;
101
+ nodes = findRelatedNodes(hasRelatedRow)
102
+ ) {
103
+ await fetch(nodes, { areRelated: true });
104
+ }
105
+
106
+ // The other related models only need to exist, except for `#ref` models,
107
+ // which are the models that they refer to, and the rows of the current graph
108
+ // (#372):
109
+ const nodes = findRelatedNodes((node, rowKeys) => !node.isReference && !rowKeys.has(node.rowKey));
110
+
111
+ for (const nodesOfClass of groupBy(nodes, (node) => node.modelClass).values()) {
112
+ const models = await fetchModels(builder, plan.graphOptions, nodesOfClass, {
113
+ onlyIds: true,
114
+ });
115
+ const idKeys = new Set(models.map((model) => model.$idKey()));
116
+
117
+ for (const node of nodesOfClass) {
118
+ node.isMissing = !idKeys.has(node.obj.$idKey());
119
+ }
120
+ }
121
+
122
+ return movedNodes;
123
+ }
124
+
125
+ function pair(node, currentNode) {
126
+ node.current ??= currentNode;
127
+ currentNode.node = node;
128
+ pairChildren(node, currentNode);
129
+ }
130
+
131
+ // Pair the related models with ids with the current rows in the same relations.
132
+ // Rows can be related more than once through duplicate join rows, and all of
133
+ // them pair with the same model.
134
+ function pairChildren(node, currentNode) {
135
+ const childrenByRelation = groupBy(currentNode.children, (child) => child.relation.name);
136
+
137
+ for (const child of node.children) {
138
+ if (child.obj.$hasId()) {
139
+ const key = child.obj.$idKey();
140
+
141
+ for (const currentChild of childrenByRelation.get(child.relation.name) || []) {
142
+ if (currentChild.obj.$idKey() === key) {
143
+ pair(child, currentChild);
144
+ }
145
+ }
146
+ }
147
+ }
148
+ }
149
+
150
+ function isMoved(node) {
151
+ return node.relation.keySide === 'related';
152
+ }
153
+
154
+ function hasRelations(obj) {
155
+ return obj.constructor.getRelationNames().some((name) => obj[name] !== undefined);
156
+ }
157
+
158
+ // Fetch the rows of `nodes`, with their relations unless they're moved, or only
159
+ // their ids, in as many queries as the number of their ids requires.
160
+ async function fetchModels(builder, graphOptions, nodes, options) {
161
+ const [{ modelClass }] = nodes;
162
+ const uniqueNodes = uniqBy(nodes, (node) => node.obj.$idKey());
163
+ const maxIds = getMaxRows(builder.unsafeKnex(), modelClass.getIdColumnArray().length);
164
+ const models = [];
165
+
166
+ for (const chunkNodes of chunk(uniqueNodes, maxIds)) {
167
+ models.push(...(await fetchModelsOfNodes(builder, graphOptions, chunkNodes, options)));
168
+ }
169
+
170
+ return models;
171
+ }
172
+
173
+ function fetchModelsOfNodes(
174
+ builder,
175
+ graphOptions,
176
+ nodes,
177
+ { withRelations = false, onlyIds = false },
178
+ ) {
179
+ const [{ modelClass }] = nodes;
180
+ const ids = nodes.map((node) => node.obj.$id());
181
+ const expression = RelationExpression.fromModelGraph(
182
+ withRelations ? nodes.map((node) => node.obj) : [],
183
+ );
184
+
185
+ return modelClass
186
+ .query()
187
+ .childQueryOf(builder, { fork: true, isInternalQuery: true })
188
+ .modify((fetchBuilder) => {
189
+ // Lock the fetched rows like the upsert query asks for.
190
+ for (const method of ['forUpdate', 'forShare', 'forNoKeyUpdate', 'forKeyShare']) {
191
+ if (builder.has(method)) {
192
+ fetchBuilder[method]();
193
+ }
194
+ }
195
+ })
196
+ .modify((fetchBuilder) => {
197
+ const nodesByRelationPath = withRelations
198
+ ? getNodesByRelationPath(nodes, expression)
199
+ : new Map([['', nodes]]);
200
+
201
+ for (const [relationPath, pathNodes] of nodesByRelationPath) {
202
+ const select = onlyIds
203
+ ? (builder) => builder.select(builder.fullIdColumn())
204
+ : createSelect(pathNodes, graphOptions, !relationPath);
205
+
206
+ if (!relationPath) {
207
+ fetchBuilder.modify(select);
208
+ } else {
209
+ fetchBuilder.modifyGraph(relationPath, select);
210
+ }
211
+ }
212
+ })
213
+ .findByIds(ids)
214
+ .withGraphFetched(expression)
215
+ .internalOptions({ keepImplicitJoinProps: true });
216
+ }
217
+
218
+ // Group the fetched nodes and their descendants by their relation paths below
219
+ // the fetched nodes. Relations with nulls or empty arrays have no nodes, but
220
+ // are fetched too.
221
+ function getNodesByRelationPath(nodes, expression) {
222
+ const nodesByRelationPath = new Map();
223
+
224
+ for (const node of nodes) {
225
+ const depth = node.relationPath.length;
226
+
227
+ for (const descendant of [node, ...node.descendants]) {
228
+ const relationPath = descendant.relationPath.slice(depth).join('.');
229
+ const pathNodes = nodesByRelationPath.get(relationPath);
230
+
231
+ if (pathNodes) {
232
+ pathNodes.push(descendant);
233
+ } else {
234
+ nodesByRelationPath.set(relationPath, [descendant]);
235
+ }
236
+ }
237
+ }
238
+
239
+ const addPaths = (expressionNode, path) => {
240
+ for (const name of expressionNode.$childNames) {
241
+ const relationPath = [...path, name];
242
+ const key = relationPath.join('.');
243
+
244
+ if (!nodesByRelationPath.has(key)) {
245
+ nodesByRelationPath.set(key, []);
246
+ }
247
+
248
+ addPaths(expressionNode[name], relationPath);
249
+ }
250
+ };
251
+
252
+ addPaths(expression.node, []);
253
+ return nodesByRelationPath;
254
+ }
255
+
256
+ function createSelect(nodes, graphOptions, isFetchRoot) {
257
+ if (graphOptions.isFetchStrategy(FetchStrategy.OnlyIdentifiers)) {
258
+ return (builder) => builder.select(builder.fullIdColumn());
259
+ } else if (graphOptions.isFetchStrategy(FetchStrategy.OnlyNeeded)) {
260
+ return (builder) => builder.select(getInputColumnSelections(builder, nodes, isFetchRoot));
261
+ } else {
262
+ return () => {};
263
+ }
264
+ }
265
+
266
+ // Select only the columns that the graph gives, and always the ids. Related
267
+ // models are fetched without the join tables of their relations, so their join
268
+ // table extras are left out at the root of the fetch.
269
+ function getInputColumnSelections(builder, nodes, isFetchRoot) {
270
+ const selections = new Map();
271
+
272
+ for (const node of nodes) {
273
+ const declaredColumns = getDeclaredColumns(node.modelClass);
274
+
275
+ for (const key of Object.keys(node.obj.$toDatabaseJson(builder))) {
276
+ // Select the json column of field expressions like `'meta:a'`:
277
+ const column = getFieldExpressionColumn(key, declaredColumns) ?? key;
278
+
279
+ if (!selections.has(column)) {
280
+ const { relation } = node;
281
+ const extra =
282
+ relation?.keySide === 'join' &&
283
+ relation.joinTableExtras.find((extra) => extra.aliasCol === column);
284
+
285
+ if (!extra) {
286
+ selections.set(column, `${builder.tableRefFor(node.modelClass)}.${column}`);
287
+ } else if (!isFetchRoot) {
288
+ const alias = relation.joinTableAliasFor(builder);
289
+ selections.set(column, `${alias}.${extra.joinTableCol} as ${extra.aliasCol}`);
290
+ }
291
+ }
292
+ }
293
+ }
294
+
295
+ const values = [...selections.values()];
296
+
297
+ for (const idColumn of asArray(builder.fullIdColumn())) {
298
+ if (!values.includes(idColumn)) {
299
+ values.push(idColumn);
300
+ }
301
+ }
302
+
303
+ return values;
304
+ }