@abedmurrar/objection 0.0.0-stage → 4.0.0-beta.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (190) hide show
  1. package/LICENSE +23 -0
  2. package/README.md +105 -2
  3. package/lib/index.mjs +6 -0
  4. package/lib/initialize.js +11 -0
  5. package/lib/model/AjvValidator.js +278 -0
  6. package/lib/model/Model.js +772 -0
  7. package/lib/model/ModifierNotFoundError.js +10 -0
  8. package/lib/model/NotFoundError.js +19 -0
  9. package/lib/model/RelationDoesNotExistError.js +11 -0
  10. package/lib/model/ValidationError.js +46 -0
  11. package/lib/model/Validator.js +20 -0
  12. package/lib/model/getModel.js +7 -0
  13. package/lib/model/graph/ModelGraph.js +60 -0
  14. package/lib/model/graph/ModelGraphBuilder.js +188 -0
  15. package/lib/model/graph/ModelGraphEdge.js +40 -0
  16. package/lib/model/graph/ModelGraphNode.js +201 -0
  17. package/lib/model/inheritModel.js +19 -0
  18. package/lib/model/modelBindKnex.js +69 -0
  19. package/lib/model/modelClone.js +102 -0
  20. package/lib/model/modelColPropMap.js +24 -0
  21. package/lib/model/modelConcurrency.js +24 -0
  22. package/lib/model/modelId.js +57 -0
  23. package/lib/model/modelJsonAttributes.js +72 -0
  24. package/lib/model/modelParseRelations.js +83 -0
  25. package/lib/model/modelQueryProps.js +116 -0
  26. package/lib/model/modelSet.js +116 -0
  27. package/lib/model/modelTableMetadata.js +71 -0
  28. package/lib/model/modelToJson.js +157 -0
  29. package/lib/model/modelUtils.js +40 -0
  30. package/lib/model/modelValidate.js +40 -0
  31. package/lib/model/modelValues.js +113 -0
  32. package/lib/model/modelVisitor.js +34 -0
  33. package/lib/objection.js +89 -0
  34. package/lib/queryBuilder/FunctionBuilder.js +291 -0
  35. package/lib/queryBuilder/InternalOptions.js +24 -0
  36. package/lib/queryBuilder/JoinBuilder.js +124 -0
  37. package/lib/queryBuilder/QueryBuilder.js +1136 -0
  38. package/lib/queryBuilder/QueryBuilderBase.js +666 -0
  39. package/lib/queryBuilder/QueryBuilderContext.js +20 -0
  40. package/lib/queryBuilder/QueryBuilderContextBase.js +26 -0
  41. package/lib/queryBuilder/QueryBuilderOperationSupport.js +484 -0
  42. package/lib/queryBuilder/QueryBuilderUserContext.js +25 -0
  43. package/lib/queryBuilder/RawBuilder.js +78 -0
  44. package/lib/queryBuilder/ReferenceBuilder.js +246 -0
  45. package/lib/queryBuilder/RelationExpression.js +373 -0
  46. package/lib/queryBuilder/StaticHookArguments.js +122 -0
  47. package/lib/queryBuilder/ValueBuilder.js +115 -0
  48. package/lib/queryBuilder/expressions.js +121 -0
  49. package/lib/queryBuilder/functions/mysql.js +762 -0
  50. package/lib/queryBuilder/graph/GraphAction.js +54 -0
  51. package/lib/queryBuilder/graph/GraphData.js +12 -0
  52. package/lib/queryBuilder/graph/GraphFetcher.js +155 -0
  53. package/lib/queryBuilder/graph/GraphNodeDbExistence.js +146 -0
  54. package/lib/queryBuilder/graph/GraphOperation.js +21 -0
  55. package/lib/queryBuilder/graph/GraphOptions.js +165 -0
  56. package/lib/queryBuilder/graph/GraphUpsert.js +231 -0
  57. package/lib/queryBuilder/graph/delete/GraphDelete.js +15 -0
  58. package/lib/queryBuilder/graph/delete/GraphDeleteAction.js +56 -0
  59. package/lib/queryBuilder/graph/insert/GraphInsert.js +116 -0
  60. package/lib/queryBuilder/graph/insert/GraphInsertAction.js +183 -0
  61. package/lib/queryBuilder/graph/insert/JoinRowGraphInsertAction.js +70 -0
  62. package/lib/queryBuilder/graph/patch/GraphPatch.js +15 -0
  63. package/lib/queryBuilder/graph/patch/GraphPatchAction.js +205 -0
  64. package/lib/queryBuilder/graph/recursiveUpsert/GraphRecursiveUpsert.js +26 -0
  65. package/lib/queryBuilder/graph/recursiveUpsert/GraphRecursiveUpsertAction.js +96 -0
  66. package/lib/queryBuilder/join/JoinResultColumn.js +19 -0
  67. package/lib/queryBuilder/join/JoinResultParser.js +106 -0
  68. package/lib/queryBuilder/join/RelationJoiner.js +258 -0
  69. package/lib/queryBuilder/join/TableNode.js +136 -0
  70. package/lib/queryBuilder/join/TableTree.js +69 -0
  71. package/lib/queryBuilder/join/utils.js +19 -0
  72. package/lib/queryBuilder/operations/DelegateOperation.js +101 -0
  73. package/lib/queryBuilder/operations/DeleteOperation.js +47 -0
  74. package/lib/queryBuilder/operations/FindByIdOperation.js +29 -0
  75. package/lib/queryBuilder/operations/FindByIdsOperation.js +23 -0
  76. package/lib/queryBuilder/operations/FindOperation.js +147 -0
  77. package/lib/queryBuilder/operations/FirstOperation.js +22 -0
  78. package/lib/queryBuilder/operations/FromOperation.js +91 -0
  79. package/lib/queryBuilder/operations/InsertAndFetchOperation.js +38 -0
  80. package/lib/queryBuilder/operations/InsertGraphAndFetchOperation.js +35 -0
  81. package/lib/queryBuilder/operations/InsertGraphOperation.js +87 -0
  82. package/lib/queryBuilder/operations/InsertOperation.js +173 -0
  83. package/lib/queryBuilder/operations/InstanceDeleteOperation.js +37 -0
  84. package/lib/queryBuilder/operations/InstanceFindOperation.js +17 -0
  85. package/lib/queryBuilder/operations/InstanceInsertOperation.js +20 -0
  86. package/lib/queryBuilder/operations/InstanceUpdateOperation.js +45 -0
  87. package/lib/queryBuilder/operations/JoinRelatedOperation.js +83 -0
  88. package/lib/queryBuilder/operations/KnexOperation.js +11 -0
  89. package/lib/queryBuilder/operations/MergeOperation.js +43 -0
  90. package/lib/queryBuilder/operations/ObjectionToKnexConvertingOperation.js +173 -0
  91. package/lib/queryBuilder/operations/OnBuildKnexOperation.js +23 -0
  92. package/lib/queryBuilder/operations/OnBuildOperation.js +23 -0
  93. package/lib/queryBuilder/operations/OnErrorOperation.js +23 -0
  94. package/lib/queryBuilder/operations/QueryBuilderOperation.js +258 -0
  95. package/lib/queryBuilder/operations/RangeOperation.js +37 -0
  96. package/lib/queryBuilder/operations/RelateOperation.js +20 -0
  97. package/lib/queryBuilder/operations/ReturningOperation.js +24 -0
  98. package/lib/queryBuilder/operations/RunAfterOperation.js +23 -0
  99. package/lib/queryBuilder/operations/RunBeforeOperation.js +23 -0
  100. package/lib/queryBuilder/operations/UnrelateOperation.js +18 -0
  101. package/lib/queryBuilder/operations/UpdateAndFetchOperation.js +63 -0
  102. package/lib/queryBuilder/operations/UpdateOperation.js +130 -0
  103. package/lib/queryBuilder/operations/UpsertGraphAndFetchOperation.js +35 -0
  104. package/lib/queryBuilder/operations/UpsertGraphOperation.js +47 -0
  105. package/lib/queryBuilder/operations/WhereCompositeOperation.js +48 -0
  106. package/lib/queryBuilder/operations/WhereInOperation.js +46 -0
  107. package/lib/queryBuilder/operations/eager/EagerOperation.js +46 -0
  108. package/lib/queryBuilder/operations/eager/JoinEagerOperation.js +41 -0
  109. package/lib/queryBuilder/operations/eager/NaiveEagerOperation.js +10 -0
  110. package/lib/queryBuilder/operations/eager/WhereInEagerOperation.js +227 -0
  111. package/lib/queryBuilder/operations/jsonApi/WhereJsonHasOperation.js +29 -0
  112. package/lib/queryBuilder/operations/jsonApi/WhereJsonNotObjectOperation.js +37 -0
  113. package/lib/queryBuilder/operations/jsonApi/WhereJsonOperation.js +30 -0
  114. package/lib/queryBuilder/operations/jsonApi/WhereJsonOverlapsOperation.js +33 -0
  115. package/lib/queryBuilder/operations/jsonApi/mysqlJsonApi.js +155 -0
  116. package/lib/queryBuilder/operations/jsonApi/postgresJsonApi.js +135 -0
  117. package/lib/queryBuilder/operations/select/SelectOperation.js +50 -0
  118. package/lib/queryBuilder/operations/select/Selection.js +112 -0
  119. package/lib/queryBuilder/operations/whereInComposite/WhereInCompositeMsSqlOperation.js +84 -0
  120. package/lib/queryBuilder/operations/whereInComposite/WhereInCompositeOperation.js +90 -0
  121. package/lib/queryBuilder/operations/whereInComposite/WhereInCompositeSqliteOperation.js +95 -0
  122. package/lib/queryBuilder/parsers/jsonFieldExpressionParser.js +787 -0
  123. package/lib/queryBuilder/parsers/relationExpressionParser.js +930 -0
  124. package/lib/queryBuilder/transformations/CompositeQueryTransformation.js +17 -0
  125. package/lib/queryBuilder/transformations/QueryTransformation.js +9 -0
  126. package/lib/queryBuilder/transformations/WrapMysqlModifySubqueryTransformation.js +57 -0
  127. package/lib/queryBuilder/transformations/index.js +9 -0
  128. package/lib/queryBuilder/whereScopes.js +199 -0
  129. package/lib/relations/Relation.js +317 -0
  130. package/lib/relations/RelationDeleteOperation.js +23 -0
  131. package/lib/relations/RelationFindOperation.js +116 -0
  132. package/lib/relations/RelationInsertOperation.js +25 -0
  133. package/lib/relations/RelationOwner.js +199 -0
  134. package/lib/relations/RelationProperty.js +207 -0
  135. package/lib/relations/RelationUpdateOperation.js +23 -0
  136. package/lib/relations/belongsToOne/BelongsToOneDeleteOperation.js +18 -0
  137. package/lib/relations/belongsToOne/BelongsToOneInsertOperation.js +33 -0
  138. package/lib/relations/belongsToOne/BelongsToOneRelateOperation.js +39 -0
  139. package/lib/relations/belongsToOne/BelongsToOneRelation.js +45 -0
  140. package/lib/relations/belongsToOne/BelongsToOneUnrelateOperation.js +46 -0
  141. package/lib/relations/hasMany/HasManyInsertOperation.js +45 -0
  142. package/lib/relations/hasMany/HasManyRelateOperation.js +48 -0
  143. package/lib/relations/hasMany/HasManyRelation.js +41 -0
  144. package/lib/relations/hasMany/HasManyUnrelateOperation.js +26 -0
  145. package/lib/relations/hasOne/HasOneRelation.js +10 -0
  146. package/lib/relations/hasOneThrough/HasOneThroughRelation.js +10 -0
  147. package/lib/relations/manyToMany/ManyToManyModifyMixin.js +82 -0
  148. package/lib/relations/manyToMany/ManyToManyRelation.js +405 -0
  149. package/lib/relations/manyToMany/ManyToManySqliteModifyMixin.js +29 -0
  150. package/lib/relations/manyToMany/delete/ManyToManyDeleteOperation.js +8 -0
  151. package/lib/relations/manyToMany/delete/ManyToManyDeleteOperationBase.js +20 -0
  152. package/lib/relations/manyToMany/delete/ManyToManyDeleteSqliteOperation.js +8 -0
  153. package/lib/relations/manyToMany/find/ManyToManyFindOperation.js +82 -0
  154. package/lib/relations/manyToMany/insert/ManyToManyInsertOperation.js +40 -0
  155. package/lib/relations/manyToMany/relate/ManyToManyRelateOperation.js +64 -0
  156. package/lib/relations/manyToMany/unrelate/ManyToManyUnrelateOperation.js +11 -0
  157. package/lib/relations/manyToMany/unrelate/ManyToManyUnrelateOperationBase.js +27 -0
  158. package/lib/relations/manyToMany/unrelate/ManyToManyUnrelateSqliteOperation.js +11 -0
  159. package/lib/relations/manyToMany/update/ManyToManyUpdateOperation.js +8 -0
  160. package/lib/relations/manyToMany/update/ManyToManyUpdateOperationBase.js +65 -0
  161. package/lib/relations/manyToMany/update/ManyToManyUpdateSqliteOperation.js +8 -0
  162. package/lib/transaction.js +66 -0
  163. package/lib/utils/assert.js +19 -0
  164. package/lib/utils/buildUtils.js +19 -0
  165. package/lib/utils/classUtils.js +9 -0
  166. package/lib/utils/cloneDeep.js +365 -0
  167. package/lib/utils/createModifier.js +37 -0
  168. package/lib/utils/deprecate.js +11 -0
  169. package/lib/utils/identifierMapping.js +188 -0
  170. package/lib/utils/internalPropUtils.js +7 -0
  171. package/lib/utils/knexUtils.js +81 -0
  172. package/lib/utils/mixin.js +18 -0
  173. package/lib/utils/mysqlJsonPath.js +70 -0
  174. package/lib/utils/normalizeIds.js +127 -0
  175. package/lib/utils/objectUtils.js +314 -0
  176. package/lib/utils/parseFieldExpression.js +73 -0
  177. package/lib/utils/postgresJsonPath.js +96 -0
  178. package/lib/utils/promiseUtils/after.js +14 -0
  179. package/lib/utils/promiseUtils/afterReturn.js +14 -0
  180. package/lib/utils/promiseUtils/index.js +15 -0
  181. package/lib/utils/promiseUtils/isPromise.js +8 -0
  182. package/lib/utils/promiseUtils/map.js +96 -0
  183. package/lib/utils/promiseUtils/mapAfterAllReturn.js +23 -0
  184. package/lib/utils/promiseUtils/try.js +19 -0
  185. package/lib/utils/resolveModel.js +104 -0
  186. package/lib/utils/tmpColumnUtils.js +7 -0
  187. package/package.json +113 -4
  188. package/typings/objection/index.d.mts +362 -0
  189. package/typings/objection/index.d.ts +4131 -0
  190. package/typings/objection/relation-expression.d.ts +237 -0
@@ -0,0 +1,17 @@
1
+ 'use strict';
2
+ const { QueryTransformation } = require('./QueryTransformation');
3
+ class CompositeQueryTransformation extends QueryTransformation {
4
+ constructor(transformations) {
5
+ super();
6
+ this.transformations = transformations;
7
+ }
8
+ onConvertQueryBuilderBase(item, builder) {
9
+ for (const transformation of this.transformations) {
10
+ item = transformation.onConvertQueryBuilderBase(item, builder);
11
+ }
12
+ return item;
13
+ }
14
+ }
15
+ module.exports = {
16
+ CompositeQueryTransformation,
17
+ };
@@ -0,0 +1,9 @@
1
+ 'use strict';
2
+ class QueryTransformation {
3
+ onConvertQueryBuilderBase(item, builder) {
4
+ return item;
5
+ }
6
+ }
7
+ module.exports = {
8
+ QueryTransformation,
9
+ };
@@ -0,0 +1,57 @@
1
+ 'use strict';
2
+ const { QueryTransformation } = require('./QueryTransformation');
3
+ const { isMySql } = require('../../utils/knexUtils');
4
+ /**
5
+ * Mysql doesn't allow queries like this:
6
+ *
7
+ * update foo set bar = 1 where id in (select id from foo)
8
+ *
9
+ * because the subquery is for the same table `foo` as the parent update query.
10
+ * The same goes for delete queries too.
11
+ *
12
+ * This transformation wraps those subqueries like this:
13
+ *
14
+ * update foo set bar = 1 where id in (select * from (select id from foo))
15
+ */
16
+ class WrapMysqlModifySubqueryTransformation extends QueryTransformation {
17
+ onConvertQueryBuilderBase(query, parentQuery) {
18
+ const knex = parentQuery.unsafeKnex();
19
+ // Cannot detect anything if, for whatever reason, a knex instance
20
+ // or a transaction is not registered at this point.
21
+ if (!knex) {
22
+ return query;
23
+ }
24
+ // This transformation only applies to MySQL.
25
+ if (!isMySql(knex)) {
26
+ return query;
27
+ }
28
+ // A subquery in a join callback belongs to the query of the join.
29
+ while (parentQuery && typeof parentQuery.isUpdate !== 'function') {
30
+ parentQuery = parentQuery.parentQuery();
31
+ }
32
+ // This transformation only applies to update and delete queries.
33
+ if (!parentQuery || (!parentQuery.isUpdate() && !parentQuery.isDelete())) {
34
+ return query;
35
+ }
36
+ // If the subquery is for another table and the query doesn't join the
37
+ // parent query's table, we're good to go.
38
+ if (parentQuery.tableName() !== query.tableName() &&
39
+ !hasJoinsToTable(query, parentQuery.tableName())) {
40
+ return query;
41
+ }
42
+ return query.modelClass().query().from(query.as('mysql_subquery_fix'));
43
+ }
44
+ }
45
+ function hasJoinsToTable(query, tableName) {
46
+ let found = false;
47
+ query.forEachOperation(query.constructor.JoinSelector, (op) => {
48
+ if (op.args[0] === tableName) {
49
+ found = true;
50
+ return false;
51
+ }
52
+ });
53
+ return found;
54
+ }
55
+ module.exports = {
56
+ WrapMysqlModifySubqueryTransformation,
57
+ };
@@ -0,0 +1,9 @@
1
+ 'use strict';
2
+ const { CompositeQueryTransformation } = require('./CompositeQueryTransformation');
3
+ const { WrapMysqlModifySubqueryTransformation, } = require('./WrapMysqlModifySubqueryTransformation');
4
+ const transformation = new CompositeQueryTransformation([
5
+ new WrapMysqlModifySubqueryTransformation(),
6
+ ]);
7
+ module.exports = {
8
+ transformation,
9
+ };
@@ -0,0 +1,199 @@
1
+ 'use strict';
2
+ const { isObject, isFunction } = require('../utils/objectUtils');
3
+ const { isKnexRaw } = require('../utils/knexUtils');
4
+ // Where clauses objection adds on its own, like the owner condition of a relation
5
+ // query or the id condition of `$query()`, are tagged with a "where scope". When
6
+ // a query has where clauses from more than one scope, the clauses of each scope
7
+ // are wrapped in parentheses so that an `orWhere` in one scope can't escape the
8
+ // conditions of the others. Without this, `$relatedQuery('pets').where(a).orWhere(b)`
9
+ // would produce `owner and a or b` and match other owners' pets.
10
+ //
11
+ // The scopes are only used when the knex query is built. The operation tree is
12
+ // never modified.
13
+ const WhereSelector = /^(where|orWhere|andWhere|find\w+)/;
14
+ // Where operations that only ever produce a single predicate or predicates
15
+ // joined with `and`. A scope that only contains these (and no raw arguments)
16
+ // doesn't need to be wrapped in parentheses.
17
+ const AND_ONLY_OPERATIONS = new Set([
18
+ 'where',
19
+ 'andWhere',
20
+ 'whereNot',
21
+ 'andWhereNot',
22
+ 'whereIn',
23
+ 'whereNotIn',
24
+ 'whereNull',
25
+ 'whereNotNull',
26
+ 'whereBetween',
27
+ 'whereNotBetween',
28
+ 'whereExists',
29
+ 'whereNotExists',
30
+ 'whereColumn',
31
+ 'andWhereColumn',
32
+ 'whereNotColumn',
33
+ 'andWhereNotColumn',
34
+ 'whereLike',
35
+ 'whereILike',
36
+ 'whereWrapped',
37
+ 'whereComposite',
38
+ 'whereInComposite',
39
+ 'whereJsonSupersetOf',
40
+ 'whereJsonSubsetOf',
41
+ 'whereJsonNotSupersetOf',
42
+ 'whereJsonNotSubsetOf',
43
+ 'whereJsonHasAny',
44
+ 'whereJsonHasAll',
45
+ 'whereJsonNotArray',
46
+ 'whereJsonNotObject',
47
+ 'whereJsonOverlaps',
48
+ 'whereJsonNotOverlaps',
49
+ 'whereJsonObject',
50
+ 'whereNotJsonObject',
51
+ 'whereJsonPath',
52
+ 'findById',
53
+ 'findByIds',
54
+ ]);
55
+ function withWhereScope(builder, callback) {
56
+ const previous = builder._whereScope;
57
+ // A fresh object works as a unique token.
58
+ builder._whereScope = {};
59
+ try {
60
+ return callback();
61
+ }
62
+ finally {
63
+ builder._whereScope = previous;
64
+ }
65
+ }
66
+ function effectiveWhereScope(op) {
67
+ while (op) {
68
+ if (op.whereScope !== null) {
69
+ return op.whereScope;
70
+ }
71
+ op = op.parentOperation;
72
+ }
73
+ return null;
74
+ }
75
+ // Returns null if no where clauses need to be grouped. Otherwise returns an
76
+ // object that tells, for each operation, whether the operation is built as
77
+ // part of a group.
78
+ function createWhereGrouping(builder) {
79
+ const scopes = new Map();
80
+ builder.forEachOperation(WhereSelector, (op) => {
81
+ if (!hasWhereAncestor(op)) {
82
+ const scope = effectiveWhereScope(op);
83
+ const ops = scopes.get(scope) || [];
84
+ ops.push(op);
85
+ scopes.set(scope, ops);
86
+ }
87
+ });
88
+ if (scopes.size < 2) {
89
+ return null;
90
+ }
91
+ const groupByFirstOp = new Map();
92
+ const groupedOps = new Set();
93
+ for (const ops of scopes.values()) {
94
+ if (ops.every(isAndOnlyTree)) {
95
+ continue;
96
+ }
97
+ groupByFirstOp.set(ops[0], ops);
98
+ for (const op of ops) {
99
+ groupedOps.add(op);
100
+ op.forEachDescendantOperation((it) => {
101
+ groupedOps.add(it);
102
+ });
103
+ }
104
+ }
105
+ if (groupedOps.size === 0) {
106
+ return null;
107
+ }
108
+ return {
109
+ isGrouped(op) {
110
+ return groupedOps.has(op);
111
+ },
112
+ // Builds the whole group if `op` is the first operation of a group.
113
+ build(op, knexBuilder) {
114
+ const ops = groupByFirstOp.get(op);
115
+ if (!ops) {
116
+ return knexBuilder;
117
+ }
118
+ return knexBuilder.where(function () {
119
+ let groupBuilder = this;
120
+ for (const groupOp of ops) {
121
+ groupBuilder = buildOperationTree(builder, groupOp, groupBuilder);
122
+ }
123
+ });
124
+ },
125
+ };
126
+ }
127
+ function buildOperationTree(builder, op, knexBuilder) {
128
+ const ops = [op];
129
+ op.forEachDescendantOperation((it) => {
130
+ ops.push(it);
131
+ });
132
+ for (const it of ops) {
133
+ if (it.hasOnBuildKnex()) {
134
+ knexBuilder =
135
+ builder.callOperationMethod(it, 'onBuildKnex', [knexBuilder, builder]) || knexBuilder;
136
+ }
137
+ }
138
+ return knexBuilder;
139
+ }
140
+ function hasWhereAncestor(op) {
141
+ for (let parent = op.parentOperation; parent; parent = parent.parentOperation) {
142
+ if (WhereSelector.test(parent.name)) {
143
+ return true;
144
+ }
145
+ }
146
+ return false;
147
+ }
148
+ function isAndOnlyTree(op) {
149
+ if (!isAndOnly(op)) {
150
+ return false;
151
+ }
152
+ let andOnly = true;
153
+ op.forEachDescendantOperation((it) => {
154
+ if (it.hasOnBuildKnex() && !isAndOnly(it)) {
155
+ andOnly = false;
156
+ return false;
157
+ }
158
+ });
159
+ return andOnly;
160
+ }
161
+ function isAndOnly(op) {
162
+ return AND_ONLY_OPERATIONS.has(op.name) && !containsRaw(op.args);
163
+ }
164
+ function containsRaw(value) {
165
+ if (Array.isArray(value)) {
166
+ return value.some(containsRaw);
167
+ }
168
+ else if (!isObject(value)) {
169
+ return false;
170
+ }
171
+ else if (value.isObjectionReferenceBuilder) {
172
+ // A reference is always a single (possibly casted) column or json path.
173
+ return false;
174
+ }
175
+ else if (value.isObjectionFunctionBuilder) {
176
+ // `fn()` calls and expressions like `ref('a').eq(1)` are wrapped in parentheses,
177
+ // so only their arguments can contain anything.
178
+ return containsRaw(value._args);
179
+ }
180
+ else if (isKnexRaw(value) || isFunction(value.toKnexRaw)) {
181
+ // Raw sql (and anything that is converted into it) can contain anything.
182
+ return true;
183
+ }
184
+ else if (value.isObjectionQueryBuilderBase || isFunction(value.toSQL)) {
185
+ // Subqueries are always wrapped in parentheses.
186
+ return false;
187
+ }
188
+ else if (Object.getPrototypeOf(value) === Object.prototype) {
189
+ return Object.values(value).some(containsRaw);
190
+ }
191
+ else {
192
+ return false;
193
+ }
194
+ }
195
+ module.exports = {
196
+ withWhereScope,
197
+ effectiveWhereScope,
198
+ createWhereGrouping,
199
+ };
@@ -0,0 +1,317 @@
1
+ 'use strict';
2
+ const { withWhereScope } = require('../queryBuilder/whereScopes');
3
+ const { RelationProperty } = require('./RelationProperty');
4
+ const { RelationFindOperation } = require('./RelationFindOperation');
5
+ const { RelationUpdateOperation } = require('./RelationUpdateOperation');
6
+ const { RelationDeleteOperation } = require('./RelationDeleteOperation');
7
+ const { resolveModel } = require('../utils/resolveModel');
8
+ const { get, isFunction } = require('../utils/objectUtils');
9
+ const { mapAfterAllReturn } = require('../utils/promiseUtils');
10
+ const { createModifier } = require('../utils/createModifier');
11
+ class Relation {
12
+ constructor(relationName, OwnerClass) {
13
+ this.name = relationName;
14
+ this.ownerModelClass = OwnerClass;
15
+ this.relatedModelClass = null;
16
+ this.ownerProp = null;
17
+ this.relatedProp = null;
18
+ this.joinTableModelClass = null;
19
+ this.joinTableOwnerProp = null;
20
+ this.joinTableRelatedProp = null;
21
+ this.joinTableBeforeInsert = null;
22
+ this.joinTableExtras = [];
23
+ this.modify = null;
24
+ this.beforeInsert = null;
25
+ }
26
+ setMapping(mapping) {
27
+ let ctx = {
28
+ name: this.name,
29
+ mapping,
30
+ ownerModelClass: this.ownerModelClass,
31
+ relatedModelClass: null,
32
+ relatedProp: null,
33
+ ownerProp: null,
34
+ modify: null,
35
+ beforeInsert: null,
36
+ forbiddenMappingProperties: this.forbiddenMappingProperties,
37
+ createError: (msg) => this.createError(msg),
38
+ };
39
+ ctx = checkForbiddenProperties(ctx);
40
+ ctx = checkOwnerModelClass(ctx);
41
+ ctx = checkRelatedModelClass(ctx);
42
+ ctx = resolveRelatedModelClass(ctx);
43
+ ctx = checkRelation(ctx);
44
+ ctx = createJoinProperties(ctx);
45
+ ctx = parseModify(ctx);
46
+ ctx = parseBeforeInsert(ctx);
47
+ this.relatedModelClass = ctx.relatedModelClass;
48
+ this.ownerProp = ctx.ownerProp;
49
+ this.relatedProp = ctx.relatedProp;
50
+ this.modify = ctx.modify;
51
+ this.beforeInsert = ctx.beforeInsert;
52
+ }
53
+ get forbiddenMappingProperties() {
54
+ return ['join.through'];
55
+ }
56
+ get joinTable() {
57
+ return this.joinTableModelClass ? this.joinTableModelClass.getTableName() : null;
58
+ }
59
+ get joinModelClass() {
60
+ return this.getJoinModelClass(this.ownerModelClass.knex());
61
+ }
62
+ getJoinModelClass(knex) {
63
+ return this.joinTableModelClass && knex !== this.joinTableModelClass.knex()
64
+ ? this.joinTableModelClass.bindKnex(knex)
65
+ : this.joinTableModelClass;
66
+ }
67
+ isOneToOne() {
68
+ return false;
69
+ }
70
+ clone() {
71
+ const relation = new this.constructor(this.name, this.ownerModelClass);
72
+ relation.relatedModelClass = this.relatedModelClass;
73
+ relation.ownerProp = this.ownerProp;
74
+ relation.relatedProp = this.relatedProp;
75
+ relation.modify = this.modify;
76
+ relation.beforeInsert = this.beforeInsert;
77
+ relation.joinTableModelClass = this.joinTableModelClass;
78
+ relation.joinTableOwnerProp = this.joinTableOwnerProp;
79
+ relation.joinTableRelatedProp = this.joinTableRelatedProp;
80
+ relation.joinTableBeforeInsert = this.joinTableBeforeInsert;
81
+ relation.joinTableExtras = this.joinTableExtras;
82
+ return relation;
83
+ }
84
+ bindKnex(knex) {
85
+ const bound = this.clone();
86
+ bound.relatedModelClass = this.relatedModelClass.bindKnex(knex);
87
+ bound.ownerModelClass = this.ownerModelClass.bindKnex(knex);
88
+ if (this.joinTableModelClass) {
89
+ bound.joinTableModelClass = this.joinTableModelClass.bindKnex(knex);
90
+ }
91
+ return bound;
92
+ }
93
+ findQuery(builder, owner) {
94
+ const relatedRefs = this.relatedProp.refs(builder);
95
+ withWhereScope(builder, () => {
96
+ owner.buildFindQuery(builder, this, relatedRefs);
97
+ });
98
+ return this.applyModify(builder);
99
+ }
100
+ applyModify(builder) {
101
+ try {
102
+ // The relation's `modify` gets its own scope so that an `orWhere`
103
+ // in it can't escape the owner condition.
104
+ return withWhereScope(builder, () => builder.modify(this.modify));
105
+ }
106
+ catch (err) {
107
+ if (err instanceof this.relatedModelClass.ModifierNotFoundError) {
108
+ throw this.createError(err.message);
109
+ }
110
+ else {
111
+ throw err;
112
+ }
113
+ }
114
+ }
115
+ join(builder, { joinOperation = 'join', relatedTableAlias = builder.tableRefFor(this.relatedModelClass), relatedJoinSelectQuery = this.relatedModelClass.query().childQueryOf(builder), relatedTable = builder.tableNameFor(this.relatedModelClass), ownerTable = builder.tableRefFor(this.ownerModelClass), } = {}) {
116
+ let relatedJoinSelect = this.applyModify(relatedJoinSelectQuery).as(relatedTableAlias);
117
+ if (relatedJoinSelect.isSelectAll()) {
118
+ // No need to join a subquery if the query is `select * from "RelatedTable"`.
119
+ relatedJoinSelect = aliasedTableName(relatedTable, relatedTableAlias);
120
+ }
121
+ return builder[joinOperation](relatedJoinSelect, (join) => {
122
+ const relatedProp = this.relatedProp;
123
+ const ownerProp = this.ownerProp;
124
+ relatedProp.forEach((i) => {
125
+ const relatedRef = relatedProp.ref(builder, i).table(relatedTableAlias);
126
+ const ownerRef = ownerProp.ref(builder, i).table(ownerTable);
127
+ join.on(relatedRef, ownerRef);
128
+ });
129
+ });
130
+ }
131
+ find(_, owner) {
132
+ return new RelationFindOperation('find', {
133
+ relation: this,
134
+ owner,
135
+ });
136
+ }
137
+ insert(_, owner) {
138
+ /* istanbul ignore next */
139
+ throw this.createError('not implemented');
140
+ }
141
+ update(_, owner) {
142
+ return new RelationUpdateOperation('update', {
143
+ relation: this,
144
+ owner,
145
+ });
146
+ }
147
+ patch(_, owner) {
148
+ return new RelationUpdateOperation('patch', {
149
+ relation: this,
150
+ owner,
151
+ modelOptions: { patch: true },
152
+ });
153
+ }
154
+ delete(_, owner) {
155
+ return new RelationDeleteOperation('delete', {
156
+ relation: this,
157
+ owner,
158
+ });
159
+ }
160
+ relate() {
161
+ /* istanbul ignore next */
162
+ throw this.createError('not implemented');
163
+ }
164
+ unrelate() {
165
+ /* istanbul ignore next */
166
+ throw this.createError('not implemented');
167
+ }
168
+ hasRelateProp(model) {
169
+ return model.$hasProps(this.relatedProp.props);
170
+ }
171
+ setRelateProp(model, values) {
172
+ this.relatedProp.forEach((i) => {
173
+ this.relatedProp.setProp(model, i, values[i]);
174
+ });
175
+ }
176
+ executeBeforeInsert(models, queryContext, result) {
177
+ return mapAfterAllReturn(models, (model) => this.beforeInsert(model, queryContext), result);
178
+ }
179
+ createError(message) {
180
+ if (this.ownerModelClass && this.ownerModelClass.name && this.name) {
181
+ return new Error(`${this.ownerModelClass.name}.relationMappings.${this.name}: ${message}`);
182
+ }
183
+ else {
184
+ return new Error(`${this.constructor.name}: ${message}`);
185
+ }
186
+ }
187
+ }
188
+ Object.defineProperties(Relation, {
189
+ isObjectionRelationClass: {
190
+ enumerable: false,
191
+ writable: false,
192
+ value: true,
193
+ },
194
+ });
195
+ Object.defineProperties(Relation.prototype, {
196
+ isObjectionRelation: {
197
+ enumerable: false,
198
+ writable: false,
199
+ value: true,
200
+ },
201
+ });
202
+ function checkForbiddenProperties(ctx) {
203
+ ctx.forbiddenMappingProperties.forEach((prop) => {
204
+ if (get(ctx.mapping, prop.split('.')) !== undefined) {
205
+ throw ctx.createError(`Property ${prop} is not supported for this relation type.`);
206
+ }
207
+ });
208
+ return ctx;
209
+ }
210
+ function checkOwnerModelClass(ctx) {
211
+ if (!isFunction(ctx.ownerModelClass) || !ctx.ownerModelClass.isObjectionModelClass) {
212
+ throw ctx.createError(`Relation's owner is not a subclass of Model`);
213
+ }
214
+ return ctx;
215
+ }
216
+ function checkRelatedModelClass(ctx) {
217
+ if (!ctx.mapping.modelClass) {
218
+ throw ctx.createError('modelClass is not defined');
219
+ }
220
+ return ctx;
221
+ }
222
+ function resolveRelatedModelClass(ctx) {
223
+ let relatedModelClass;
224
+ try {
225
+ relatedModelClass = resolveModel(ctx.mapping.modelClass, ctx.ownerModelClass.modelPaths, 'modelClass');
226
+ }
227
+ catch (err) {
228
+ throw ctx.createError(err.message);
229
+ }
230
+ return Object.assign(ctx, { relatedModelClass });
231
+ }
232
+ function checkRelation(ctx) {
233
+ if (!ctx.mapping.relation) {
234
+ throw ctx.createError('relation is not defined');
235
+ }
236
+ if (!isFunction(ctx.mapping.relation) || !ctx.mapping.relation.isObjectionRelationClass) {
237
+ throw ctx.createError('relation is not a subclass of Relation');
238
+ }
239
+ return ctx;
240
+ }
241
+ function createJoinProperties(ctx) {
242
+ const mapping = ctx.mapping;
243
+ if (!mapping.join || !mapping.join.from || !mapping.join.to) {
244
+ throw ctx.createError('join must be an object that maps the columns of the related models together. For example: {from: "SomeTable.id", to: "SomeOtherTable.someModelId"}');
245
+ }
246
+ const fromProp = createRelationProperty(ctx, mapping.join.from, 'join.from');
247
+ const toProp = createRelationProperty(ctx, mapping.join.to, 'join.to');
248
+ let ownerProp;
249
+ let relatedProp;
250
+ if (fromProp.modelClass.getTableName() === ctx.ownerModelClass.getTableName()) {
251
+ ownerProp = fromProp;
252
+ relatedProp = toProp;
253
+ }
254
+ else if (toProp.modelClass.getTableName() === ctx.ownerModelClass.getTableName()) {
255
+ ownerProp = toProp;
256
+ relatedProp = fromProp;
257
+ }
258
+ else {
259
+ throw ctx.createError('join: either `from` or `to` must point to the owner model table.');
260
+ }
261
+ if (ownerProp.props.some((it) => it === ctx.name)) {
262
+ throw ctx.createError(`join: relation name and join property '${ctx.name}' cannot have the same name. If you cannot change one or the other, you can use $parseDatabaseJson and $formatDatabaseJson methods to convert the column name.`);
263
+ }
264
+ if (relatedProp.modelClass.getTableName() !== ctx.relatedModelClass.getTableName()) {
265
+ throw ctx.createError('join: either `from` or `to` must point to the related model table.');
266
+ }
267
+ return Object.assign(ctx, { ownerProp, relatedProp });
268
+ }
269
+ function createRelationProperty(ctx, refString, propName) {
270
+ try {
271
+ return new RelationProperty(refString, (table) => {
272
+ return [ctx.ownerModelClass, ctx.relatedModelClass].find((it) => it.getTableName() === table);
273
+ });
274
+ }
275
+ catch (err) {
276
+ if (err instanceof RelationProperty.ModelNotFoundError) {
277
+ throw ctx.createError(`join: either \`from\` or \`to\` must point to the owner model table and the other one to the related table. It might be that specified table '${err.tableName}' is not correct`);
278
+ }
279
+ else if (err instanceof RelationProperty.InvalidReferenceError) {
280
+ throw ctx.createError(`${propName} must have format TableName.columnName. For example "SomeTable.id" or in case of composite key ["SomeTable.a", "SomeTable.b"].`);
281
+ }
282
+ else {
283
+ throw err;
284
+ }
285
+ }
286
+ }
287
+ function parseModify(ctx) {
288
+ const mapping = ctx.mapping;
289
+ const modifier = mapping.modify || mapping.filter;
290
+ const modify = modifier &&
291
+ createModifier({
292
+ modifier,
293
+ modelClass: ctx.relatedModelClass,
294
+ });
295
+ return Object.assign(ctx, { modify });
296
+ }
297
+ function parseBeforeInsert(ctx) {
298
+ let beforeInsert;
299
+ if (isFunction(ctx.mapping.beforeInsert)) {
300
+ beforeInsert = ctx.mapping.beforeInsert;
301
+ }
302
+ else {
303
+ beforeInsert = (model) => model;
304
+ }
305
+ return Object.assign(ctx, { beforeInsert });
306
+ }
307
+ function aliasedTableName(tableName, alias) {
308
+ if (tableName === alias) {
309
+ return tableName;
310
+ }
311
+ else {
312
+ return `${tableName} as ${alias}`;
313
+ }
314
+ }
315
+ module.exports = {
316
+ Relation,
317
+ };
@@ -0,0 +1,23 @@
1
+ 'use strict';
2
+ const { DeleteOperation } = require('../queryBuilder/operations/DeleteOperation');
3
+ const { RelationFindOperation } = require('./RelationFindOperation');
4
+ class RelationDeleteOperation extends DeleteOperation {
5
+ constructor(name, opt) {
6
+ super(name, opt);
7
+ this.relation = opt.relation;
8
+ this.owner = opt.owner;
9
+ }
10
+ onBuild(builder) {
11
+ super.onBuild(builder);
12
+ this.relation.findQuery(builder, this.owner);
13
+ }
14
+ toFindOperation() {
15
+ return new RelationFindOperation('find', {
16
+ relation: this.relation,
17
+ owner: this.owner,
18
+ });
19
+ }
20
+ }
21
+ module.exports = {
22
+ RelationDeleteOperation,
23
+ };