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
@@ -0,0 +1,395 @@
1
+ // What a graph insert or upsert does with each model of the graph and each row
2
+ // of the current graph: The rules that decide it, and the pruning of branches
3
+ // that nothing is written in.
4
+ export class GraphPlan {
5
+ constructor({ nodes, graphOptions, keptNodes = new Map() }) {
6
+ this.nodes = nodes;
7
+ this.currentNodes = [];
8
+ this.deferredNodes = new Set();
9
+ // The nodes that earlier passes of the graph decided to keep, by row:
10
+ this.earlierKeptNodes = keptNodes;
11
+ this.graphOptions = graphOptions;
12
+ this.actions = null;
13
+ // The decisions only change while the current graph is loaded, and are
14
+ // cached once it is, see `cacheDecisions()`:
15
+ this._intents = null;
16
+ this._nodeActions = null;
17
+ this._activeNodes = null;
18
+ this._activeCurrentNodes = null;
19
+ this._keptNodesByRowKey = null;
20
+ }
21
+
22
+ get activeNodes() {
23
+ return (this._activeNodes ??= this.nodes.filter((node) => !node.isPruned));
24
+ }
25
+
26
+ get activeCurrentNodes() {
27
+ return (this._activeCurrentNodes ??= this.currentNodes.filter(
28
+ (currentNode) => !currentNode.isPruned,
29
+ ));
30
+ }
31
+
32
+ // Leave `nodes` to a later pass of the graph.
33
+ defer(nodes) {
34
+ this.deferredNodes = new Set(nodes);
35
+ this.nodes = this.nodes.filter((node) => !this.deferredNodes.has(node));
36
+ this._activeNodes = null;
37
+ }
38
+
39
+ cacheDecisions() {
40
+ this._intents = new Map();
41
+ this._nodeActions = new Map();
42
+ }
43
+
44
+ // What the data asks to do with `node`, before the options that disable it:
45
+ // Models marked with `#unrelate` or `#delete` are removed, references and
46
+ // models with ids that the options let relate are related, models at their
47
+ // place are patched, and new models are inserted. Models with ids that are
48
+ // neither are `'missing'`.
49
+ intentFor(node) {
50
+ let intent = this._intents?.get(node);
51
+
52
+ if (!intent) {
53
+ intent = this._getIntent(node);
54
+ this._intents?.set(node, intent);
55
+ }
56
+
57
+ return intent;
58
+ }
59
+
60
+ _getIntent(node) {
61
+ const insertMissing = this.graphOptions.has(node, 'insertMissing');
62
+
63
+ return node.toBeUnrelatedOrDeleted
64
+ ? 'remove'
65
+ : node.isReference
66
+ ? 'relate'
67
+ : node.isMissing
68
+ ? insertMissing
69
+ ? 'insert'
70
+ : this.findInsertingNode(node)
71
+ ? 'relate'
72
+ : 'missing'
73
+ : node.isDbReference || this._canRelate(node)
74
+ ? 'relate'
75
+ : node.current
76
+ ? 'patch'
77
+ : !node.hasId || insertMissing
78
+ ? 'insert'
79
+ : 'missing';
80
+ }
81
+
82
+ // Return true if the graph writes the branch below `node`: below models at
83
+ // their place, inserted models, related models with fetched rows and `#ref`
84
+ // models, but not below removed or ignored ones.
85
+ writesBranchOf(node) {
86
+ const action = this.actionFor(node);
87
+
88
+ return (
89
+ !node.toBeUnrelatedOrDeleted &&
90
+ (!!node.current ||
91
+ action === 'insert' ||
92
+ (action === 'relate' && (!!node.row || node.isReference)))
93
+ );
94
+ }
95
+
96
+ // Find the model of the graph that inserts the missing row of `node` with its
97
+ // id, so that `node` relates it once it's inserted. Models that are missing
98
+ // themselves aren't inserted.
99
+ findInsertingNode(node) {
100
+ return this.nodes.find(
101
+ (other) =>
102
+ other !== node &&
103
+ other.rowKey === node.rowKey &&
104
+ !(other.isMissing && !this.graphOptions.has(other, 'insertMissing')) &&
105
+ this.actionFor(other) === 'insert',
106
+ );
107
+ }
108
+
109
+ // What the graph does with `node`: `'insert'`, `'relate'`, `'patch'` or
110
+ // `'ignore'`. Once the writes start, read `actions` instead.
111
+ actionFor(node) {
112
+ let action = this._nodeActions?.get(node);
113
+
114
+ if (!action) {
115
+ action = this._getAction(node);
116
+ this._nodeActions?.set(node, action);
117
+ }
118
+
119
+ return action;
120
+ }
121
+
122
+ _getAction(node) {
123
+ const { graphOptions } = this;
124
+
125
+ switch (this.intentFor(node)) {
126
+ case 'insert':
127
+ return graphOptions.has(node, 'noInsert') ? 'ignore' : 'insert';
128
+ case 'relate':
129
+ return !graphOptions.has(node, 'noRelate') ? 'relate' : node.current ? 'patch' : 'ignore';
130
+ case 'patch':
131
+ return 'patch';
132
+ default:
133
+ return 'ignore';
134
+ }
135
+ }
136
+
137
+ // What the graph does with the row of `currentNode`: `'delete'`,
138
+ // `'unrelate'` or `'keep'`.
139
+ actionForCurrent(currentNode) {
140
+ const { node } = currentNode;
141
+ const has = (optionName) => this.graphOptions.has(currentNode, optionName);
142
+
143
+ if (node) {
144
+ // `#unrelate` takes precedence over `#delete` if both are set.
145
+ return node.toBeUnrelated ? 'unrelate' : node.toBeDeleted ? 'delete' : 'keep';
146
+ } else if (this._hasIgnoredOtherModel(currentNode)) {
147
+ return 'keep';
148
+ } else {
149
+ const keptNode = this.keptNodeForRow(currentNode);
150
+
151
+ if (keptNode && !this._isUnrelatedWhenKept(currentNode, keptNode)) {
152
+ return 'keep';
153
+ } else if (keptNode || has('unrelate')) {
154
+ // Rows that the graph keeps in another place must not be deleted
155
+ // (#214). They're unrelated instead, which replaces the delete, so
156
+ // `noDelete` disables it too, unless the `unrelate` option asks for it.
157
+ return has('noUnrelate') || (!has('unrelate') && has('noDelete')) ? 'keep' : 'unrelate';
158
+ } else {
159
+ return has('noDelete') ? 'keep' : 'delete';
160
+ }
161
+ }
162
+ }
163
+
164
+ // Leave out the branches that require no writes. We never do anything below
165
+ // models that are removed, and we never delete recursively.
166
+ prune() {
167
+ const isInsertOnly = this.graphOptions.isInsertOnly();
168
+ const passNodes = new Set([...this.nodes, ...this.currentNodes]);
169
+ // The rows that were fetched for pruned nodes are left alone too. Nodes of
170
+ // later passes aren't pruned here:
171
+ const pruneNodes = (nodes) => {
172
+ for (const node of nodes.filter((node) => passNodes.has(node))) {
173
+ node.isPruned = true;
174
+
175
+ for (const row of node.row ? [node.row, ...node.row.descendants] : []) {
176
+ row.isPruned = true;
177
+ }
178
+ }
179
+
180
+ this._activeNodes = null;
181
+ this._activeCurrentNodes = null;
182
+ };
183
+ const pruneDescendants = (nodes) => pruneNodes(nodes.flatMap((node) => node.descendants));
184
+
185
+ // Nothing is written below models that are neither inserted nor at their
186
+ // place. `upsertGraph()` upserts the branches below related models, whose
187
+ // rows were fetched for that, and below `#ref` models of new models, but
188
+ // `insertGraph()` leaves them out (#217).
189
+ pruneDescendants(
190
+ this.activeNodes.filter(
191
+ (node) =>
192
+ !node.current &&
193
+ this.intentFor(node) !== 'insert' &&
194
+ !(
195
+ this.actionFor(node) === 'relate' &&
196
+ (node.row || (node.isReference && !this.graphOptions.isInsertOnly()))
197
+ ),
198
+ ),
199
+ );
200
+
201
+ // Nothing is written below models that `noInsert` skips, as their
202
+ // descendants would be orphans (#215), except for the branches of their
203
+ // children that the references of written models need: the ones that
204
+ // models outside of all these branches point into, and then the ones that
205
+ // the models of kept branches point into. The references of ignored models
206
+ // are never resolved.
207
+ const isSkipped = (node) =>
208
+ this.intentFor(node) === 'insert' && this.actionFor(node) === 'ignore';
209
+ const isBranch = (node) => !!node.parent && isSkipped(node.parent);
210
+ const getBranches = (node) => {
211
+ const branches = [];
212
+
213
+ for (let ancestor = node; ancestor; ancestor = ancestor.parent) {
214
+ if (isBranch(ancestor)) {
215
+ branches.push(ancestor);
216
+ }
217
+ }
218
+
219
+ return branches;
220
+ };
221
+ const getReferencedNodes = (node) =>
222
+ this.actionFor(node) === 'ignore'
223
+ ? []
224
+ : [node.referencedNode, ...node.propertyReferences.map((ref) => ref.referencedNode)].filter(
225
+ (referencedNode) => referencedNode && !referencedNode.isPruned,
226
+ );
227
+
228
+ const keptBranches = new Set();
229
+ const visitedNodes = new Set();
230
+ const getNewKeptNodes = () =>
231
+ this.activeNodes.filter(
232
+ (node) =>
233
+ !visitedNodes.has(node) && getBranches(node).every((branch) => keptBranches.has(branch)),
234
+ );
235
+
236
+ for (let nodes = getNewKeptNodes(); nodes.length > 0; nodes = getNewKeptNodes()) {
237
+ for (const node of nodes) {
238
+ visitedNodes.add(node);
239
+
240
+ for (const branch of getReferencedNodes(node).flatMap(getBranches)) {
241
+ keptBranches.add(branch);
242
+ }
243
+ }
244
+ }
245
+
246
+ pruneNodes(
247
+ this.activeNodes
248
+ .filter((node) => isBranch(node) && !keptBranches.has(node))
249
+ .flatMap((node) => [node, ...node.descendants]),
250
+ );
251
+
252
+ if (!isInsertOnly) {
253
+ // Rows that the graph removes are removed without their descendants. Don't
254
+ // remove rows in relations that the graph doesn't mention.
255
+ const removedNodes = this.activeCurrentNodes.filter((currentNode) => !currentNode.node);
256
+ pruneDescendants(removedNodes);
257
+
258
+ for (const currentNode of removedNodes) {
259
+ const parentNode = currentNode.parent?.node;
260
+
261
+ if (parentNode && parentNode.obj[currentNode.relation.name] === undefined) {
262
+ currentNode.isPruned = true;
263
+ this._activeCurrentNodes = null;
264
+ }
265
+ }
266
+
267
+ // Models marked with `#unrelate` or `#delete` are unrelated or deleted as
268
+ // a whole, in the graph and in the current graph.
269
+ const markedNodes = this.activeNodes.filter((node) => node.toBeUnrelatedOrDeleted);
270
+ pruneDescendants(markedNodes);
271
+ pruneDescendants(markedNodes.map((node) => node.current).filter(Boolean));
272
+ }
273
+
274
+ return this;
275
+ }
276
+
277
+ // Decide what to do with each node once, before the first write, since the
278
+ // writes change the models that the rules read. Only call this after
279
+ // pruning.
280
+ plan() {
281
+ const actions = new Map();
282
+
283
+ for (const node of this.activeNodes) {
284
+ actions.set(node, this.actionFor(node));
285
+ }
286
+
287
+ for (const currentNode of this.activeCurrentNodes) {
288
+ actions.set(currentNode, this.actionForCurrent(currentNode));
289
+ }
290
+
291
+ this.actions = actions;
292
+ return this;
293
+ }
294
+
295
+ _canRelate(node) {
296
+ return (
297
+ this.graphOptions.has(node, 'relate') &&
298
+ !node.current &&
299
+ !node.isRoot &&
300
+ hasRelateProp(node) &&
301
+ !node.isMissing
302
+ );
303
+ }
304
+
305
+ // An ignored model that isn't the current model of a to-one relation doesn't
306
+ // replace it, so we keep the current one instead of removing it as missing.
307
+ _hasIgnoredOtherModel(currentNode) {
308
+ const { relation } = currentNode;
309
+ const parentNode = currentNode.parent?.node;
310
+
311
+ return (
312
+ relation.isOneToOne() &&
313
+ !!parentNode &&
314
+ parentNode.activeChildren.some(
315
+ (child) => child.relation === relation && this.actionFor(child) === 'ignore',
316
+ )
317
+ );
318
+ }
319
+
320
+ // A row that the graph keeps in another place is unrelated here, unless the
321
+ // other place relates or patches it through the same key column: Then that
322
+ // place writes the key, which may not be nullable. A has-one row that a
323
+ // later pass moves is still unrelated if this pass writes another row into
324
+ // its slot, as the foreign key may be unique (#398).
325
+ _isUnrelatedWhenKept(currentNode, keptNode) {
326
+ const { relation } = currentNode;
327
+ return (
328
+ relation.keySide !== 'related' ||
329
+ !hasSameKey(relation, keptNode.relation) ||
330
+ (this.deferredNodes.has(keptNode) && this._hasNewRowInSlot(currentNode))
331
+ );
332
+ }
333
+
334
+ // Return true if this pass inserts or relates a row into the slot of the
335
+ // has-one relation that `currentNode` is in.
336
+ _hasNewRowInSlot(currentNode) {
337
+ return (
338
+ currentNode.relation.isOneToOne() &&
339
+ this.nodes.some(
340
+ (node) =>
341
+ node.relation === currentNode.relation &&
342
+ node.parent.existing === currentNode.parent &&
343
+ ['insert', 'relate'].includes(this.actionFor(node)),
344
+ )
345
+ );
346
+ }
347
+
348
+ // Find the node that keeps the row of `currentNode` in another place of the
349
+ // graph, by relating or patching it there, also in another pass.
350
+ keptNodeForRow(currentNode) {
351
+ return this.keptNodes.get(currentNode.rowKey) || null;
352
+ }
353
+
354
+ // The nodes that keep their rows, by row, also the ones that earlier passes
355
+ // decided to keep.
356
+ get keptNodes() {
357
+ if (!this._keptNodesByRowKey) {
358
+ this._keptNodesByRowKey = new Map(this.earlierKeptNodes);
359
+
360
+ for (const node of [...this.activeNodes, ...this.deferredNodes]) {
361
+ const action = this.actionFor(node);
362
+
363
+ if (node.obj.$hasId() && (action === 'relate' || action === 'patch')) {
364
+ this._keptNodesByRowKey.set(node.rowKey, node);
365
+ }
366
+ }
367
+ }
368
+
369
+ return this._keptNodesByRowKey;
370
+ }
371
+ }
372
+
373
+ // Return true if both relations keep the keys of their related rows in the same
374
+ // columns, also the same JSON paths of them.
375
+ function hasSameKey(relation, otherRelation) {
376
+ const { relatedProp } = relation;
377
+ const otherProp = otherRelation?.keySide === 'related' ? otherRelation.relatedProp : null;
378
+ return (
379
+ !!otherProp &&
380
+ relation.relatedModelClass.getTableName() === otherRelation.relatedModelClass.getTableName() &&
381
+ relatedProp.size === otherProp.size &&
382
+ relatedProp.props.every(
383
+ (_, i) => relatedProp.propDescription(i) === otherProp.propDescription(i),
384
+ )
385
+ );
386
+ }
387
+
388
+ // For `HasMany` and `HasOne` relations, the relate prop is the id of the
389
+ // related model. Use the id state of the input graph, since the parent's
390
+ // insert copies the foreign key into the related model, which can complete
391
+ // a composite id that contains it and would turn an insert into a relate (#2544).
392
+ function hasRelateProp(node) {
393
+ const { relation } = node;
394
+ return relation.keySide === 'related' ? node.hasId : relation.hasRelateProp(node.obj);
395
+ }