objection 3.9.1 → 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.
- package/lib/dbErrors/LICENSE +21 -0
- package/lib/dbErrors/errorCodes/mssql.js +19 -0
- package/lib/dbErrors/errorCodes/mysql.js +805 -0
- package/lib/dbErrors/errorCodes/postgres.js +241 -0
- package/lib/dbErrors/errors/CheckViolationError.js +10 -0
- package/lib/dbErrors/errors/ConstraintViolationError.js +3 -0
- package/lib/dbErrors/errors/DBError.js +9 -0
- package/lib/dbErrors/errors/DataError.js +3 -0
- package/lib/dbErrors/errors/ForeignKeyViolationError.js +11 -0
- package/lib/dbErrors/errors/NotNullViolationError.js +12 -0
- package/lib/dbErrors/errors/UniqueViolationError.js +12 -0
- package/lib/dbErrors/index.js +59 -0
- package/lib/dbErrors/parsers/mssql/DBError/CheckViolationError/parser.js +28 -0
- package/lib/dbErrors/parsers/mssql/DBError/DataError/parser.js +25 -0
- package/lib/dbErrors/parsers/mssql/DBError/ForeignKeyViolationError/parser.js +39 -0
- package/lib/dbErrors/parsers/mssql/DBError/NotNullViolationError/parser.js +30 -0
- package/lib/dbErrors/parsers/mssql/DBError/UniqueViolationError/parser.js +53 -0
- package/lib/dbErrors/parsers/mssql/DBError/parser.js +51 -0
- package/lib/dbErrors/parsers/mssql/DBError/util.js +16 -0
- package/lib/dbErrors/parsers/mysql/DBError/ConstraintViolationError/ForeignKeyViolationError/parser.js +43 -0
- package/lib/dbErrors/parsers/mysql/DBError/ConstraintViolationError/NotNullViolationError/parser.js +29 -0
- package/lib/dbErrors/parsers/mysql/DBError/ConstraintViolationError/UniqueViolationError/parser.js +27 -0
- package/lib/dbErrors/parsers/mysql/DBError/ConstraintViolationError/parser.js +23 -0
- package/lib/dbErrors/parsers/mysql/DBError/DataError/parser.js +24 -0
- package/lib/dbErrors/parsers/mysql/DBError/parser.js +28 -0
- package/lib/dbErrors/parsers/postgres/DBError/ConstraintViolationError/CheckViolationError/parser.js +18 -0
- package/lib/dbErrors/parsers/postgres/DBError/ConstraintViolationError/ForeignKeyViolationError/parser.js +18 -0
- package/lib/dbErrors/parsers/postgres/DBError/ConstraintViolationError/NotNullViolationError/parser.js +18 -0
- package/lib/dbErrors/parsers/postgres/DBError/ConstraintViolationError/UniqueViolationError/parser.js +40 -0
- package/lib/dbErrors/parsers/postgres/DBError/ConstraintViolationError/parser.js +25 -0
- package/lib/dbErrors/parsers/postgres/DBError/DataError/parser.js +16 -0
- package/lib/dbErrors/parsers/postgres/DBError/parser.js +27 -0
- package/lib/dbErrors/parsers/sqlite/DBError/ConstraintViolationError/CheckViolationError/parser.js +20 -0
- package/lib/dbErrors/parsers/sqlite/DBError/ConstraintViolationError/ForeignKeyViolationError/parser.js +20 -0
- package/lib/dbErrors/parsers/sqlite/DBError/ConstraintViolationError/NotNullViolationError/parser.js +27 -0
- package/lib/dbErrors/parsers/sqlite/DBError/ConstraintViolationError/UniqueViolationError/parser.js +28 -0
- package/lib/dbErrors/parsers/sqlite/DBError/ConstraintViolationError/parser.js +24 -0
- package/lib/dbErrors/parsers/sqlite/DBError/parser.js +23 -0
- package/lib/dbErrors/utils/sqlState.js +3 -0
- package/lib/initialize.js +1 -7
- package/lib/model/AjvValidator.js +51 -117
- package/lib/model/Model.js +84 -59
- package/lib/model/ModifierNotFoundError.js +1 -7
- package/lib/model/NotFoundError.js +1 -7
- package/lib/model/RelationDoesNotExistError.js +1 -7
- package/lib/model/ValidationError.js +3 -10
- package/lib/model/Validator.js +1 -7
- package/lib/model/getModel.js +7 -6
- package/lib/model/inheritModel.js +1 -7
- package/lib/model/modelBindKnex.js +4 -10
- package/lib/model/modelClone.js +5 -11
- package/lib/model/modelColPropMap.js +3 -10
- package/lib/model/modelId.js +3 -10
- package/lib/model/modelJsonAttributes.js +35 -16
- package/lib/model/modelParseRelations.js +2 -8
- package/lib/model/modelQueryProps.js +11 -23
- package/lib/model/modelSet.js +89 -41
- package/lib/model/modelTableMetadata.js +4 -11
- package/lib/model/modelToJson.js +6 -13
- package/lib/model/modelUtils.js +14 -13
- package/lib/model/modelValidate.js +3 -9
- package/lib/model/modelValues.js +5 -14
- package/lib/model/modelVisitor.js +1 -7
- package/lib/objection.js +33 -71
- package/lib/queryBuilder/FunctionBuilder.js +4 -11
- package/lib/queryBuilder/InternalOptions.js +4 -9
- package/lib/queryBuilder/JoinBuilder.js +5 -8
- package/lib/queryBuilder/QueryBuilder.js +126 -95
- package/lib/queryBuilder/QueryBuilderBase.js +41 -123
- package/lib/queryBuilder/QueryBuilderContext.js +2 -8
- package/lib/queryBuilder/QueryBuilderContextBase.js +2 -8
- package/lib/queryBuilder/QueryBuilderOperationSupport.js +7 -21
- package/lib/queryBuilder/QueryBuilderUserContext.js +1 -7
- package/lib/queryBuilder/RawBuilder.js +5 -13
- package/lib/queryBuilder/ReferenceBuilder.js +20 -48
- package/lib/queryBuilder/RelationExpression.js +18 -16
- package/lib/queryBuilder/StaticHookArguments.js +74 -16
- package/lib/queryBuilder/ValueBuilder.js +11 -13
- package/lib/queryBuilder/getJoinBuilder.js +10 -0
- package/lib/queryBuilder/graph/GraphLoader.js +304 -0
- package/lib/queryBuilder/graph/GraphNode.js +235 -0
- package/lib/queryBuilder/graph/GraphOptions.js +55 -248
- package/lib/queryBuilder/graph/GraphPlan.js +395 -0
- package/lib/queryBuilder/graph/GraphUpsert.js +466 -414
- package/lib/queryBuilder/graph/GraphWriter.js +1263 -0
- package/lib/queryBuilder/join/JoinResultColumn.js +1 -7
- package/lib/queryBuilder/join/JoinResultParser.js +6 -11
- package/lib/queryBuilder/join/RelationJoiner.js +66 -84
- package/lib/queryBuilder/join/TableNode.js +2 -8
- package/lib/queryBuilder/join/TableTree.js +3 -9
- package/lib/queryBuilder/join/utils.js +5 -15
- package/lib/queryBuilder/jsonDialects/JsonDialect.js +51 -0
- package/lib/queryBuilder/jsonDialects/MySqlJsonDialect.js +81 -0
- package/lib/queryBuilder/jsonDialects/PostgresJsonDialect.js +45 -0
- package/lib/queryBuilder/jsonDialects/SqliteJsonDialect.js +67 -0
- package/lib/queryBuilder/jsonDialects/index.js +44 -0
- package/lib/queryBuilder/operations/DelegateOperation.js +2 -8
- package/lib/queryBuilder/operations/DeleteOperation.js +10 -39
- package/lib/queryBuilder/operations/FindByIdOperation.js +7 -12
- package/lib/queryBuilder/operations/FindByIdsOperation.js +4 -8
- package/lib/queryBuilder/operations/FindOperation.js +14 -34
- package/lib/queryBuilder/operations/FirstOperation.js +6 -10
- package/lib/queryBuilder/operations/FromOperation.js +3 -9
- package/lib/queryBuilder/operations/InsertAndFetchOperation.js +13 -12
- package/lib/queryBuilder/operations/InsertGraphAndFetchOperation.js +4 -10
- package/lib/queryBuilder/operations/InsertGraphOperation.js +7 -18
- package/lib/queryBuilder/operations/InsertOperation.js +118 -45
- package/lib/queryBuilder/operations/InstanceDeleteOperation.js +5 -12
- package/lib/queryBuilder/operations/InstanceFindOperation.js +4 -11
- package/lib/queryBuilder/operations/InstanceInsertOperation.js +2 -8
- package/lib/queryBuilder/operations/InstanceUpdateOperation.js +7 -14
- package/lib/queryBuilder/operations/JoinRelatedOperation.js +5 -11
- package/lib/queryBuilder/operations/KnexOperation.js +2 -8
- package/lib/queryBuilder/operations/LimitPerParentOperation.js +7 -69
- package/lib/queryBuilder/operations/MergeOperation.js +5 -11
- package/lib/queryBuilder/operations/NoneOperation.js +2 -8
- package/lib/queryBuilder/operations/ObjectionToKnexConvertingOperation.js +20 -49
- package/lib/queryBuilder/operations/OnBuildKnexOperation.js +2 -8
- package/lib/queryBuilder/operations/OnBuildOperation.js +2 -8
- package/lib/queryBuilder/operations/OnErrorOperation.js +2 -8
- package/lib/queryBuilder/operations/QueryBuilderOperation.js +1 -7
- package/lib/queryBuilder/operations/RangeOperation.js +11 -26
- package/lib/queryBuilder/operations/RelateOperation.js +2 -8
- package/lib/queryBuilder/operations/ReturningOperation.js +3 -9
- package/lib/queryBuilder/operations/RunAfterOperation.js +2 -12
- package/lib/queryBuilder/operations/RunBeforeOperation.js +2 -8
- package/lib/queryBuilder/operations/UnrelateOperation.js +2 -8
- package/lib/queryBuilder/operations/UpdateAndFetchOperation.js +14 -17
- package/lib/queryBuilder/operations/UpdateOperation.js +32 -132
- package/lib/queryBuilder/operations/UpsertGraphAndFetchOperation.js +8 -11
- package/lib/queryBuilder/operations/UpsertGraphOperation.js +6 -12
- package/lib/queryBuilder/operations/WhereCompositeOperation.js +3 -9
- package/lib/queryBuilder/operations/WhereGroupOperation.js +2 -8
- package/lib/queryBuilder/operations/eager/EagerOperation.js +3 -9
- package/lib/queryBuilder/operations/eager/JoinEagerOperation.js +72 -10
- package/lib/queryBuilder/operations/eager/NaiveEagerOperation.js +2 -8
- package/lib/queryBuilder/operations/eager/WhereInEagerOperation.js +23 -44
- package/lib/queryBuilder/operations/fieldExpressions.js +122 -0
- package/lib/queryBuilder/operations/jsonApi/WhereJsonHasOperation.js +20 -0
- package/lib/queryBuilder/operations/jsonApi/WhereJsonOperation.js +44 -0
- package/lib/queryBuilder/operations/jsonApi/WhereJsonTypeOperation.js +14 -0
- package/lib/queryBuilder/operations/select/SelectOperation.js +11 -21
- package/lib/queryBuilder/operations/select/Selection.js +19 -17
- package/lib/queryBuilder/operations/whereInComposite/WhereInCompositeMsSqlOperation.js +4 -10
- package/lib/queryBuilder/operations/whereInComposite/WhereInCompositeOperation.js +4 -10
- package/lib/queryBuilder/operations/whereInComposite/WhereInCompositeSqliteOperation.js +4 -10
- package/lib/queryBuilder/operations/windowOrder.js +56 -0
- package/lib/queryBuilder/parsers/jsonFieldExpressionParser.js +3 -7
- package/lib/queryBuilder/parsers/relationExpressionParser.js +42 -43
- package/lib/queryBuilder/parsers/relationExpressionParser.pegjs +39 -36
- package/lib/queryBuilder/transformations/CompositeQueryTransformation.js +2 -8
- package/lib/queryBuilder/transformations/QueryTransformation.js +1 -7
- package/lib/queryBuilder/transformations/WrapMysqlModifySubqueryTransformation.js +3 -9
- package/lib/queryBuilder/transformations/index.js +3 -11
- package/lib/queryBuilder/whereGroups.js +9 -24
- package/lib/queryBuilder/withoutOrder.js +4 -11
- package/lib/relations/Relation.js +47 -28
- package/lib/relations/RelationDeleteOperation.js +4 -14
- package/lib/relations/RelationFindOperation.js +119 -90
- package/lib/relations/RelationInsertOperation.js +2 -8
- package/lib/relations/RelationOwner.js +5 -11
- package/lib/relations/RelationProperty.js +4 -10
- package/lib/relations/RelationUpdateOperation.js +4 -14
- package/lib/relations/RelationWriteResultOperation.js +15 -0
- package/lib/relations/belongsToMany/BelongsToManyDeleteOperation.js +23 -0
- package/lib/relations/belongsToMany/BelongsToManyFindOperation.js +35 -0
- package/lib/relations/belongsToMany/BelongsToManyInsertOperation.js +28 -0
- package/lib/relations/belongsToMany/BelongsToManyRelateOperation.js +16 -0
- package/lib/relations/belongsToMany/BelongsToManyRelation.js +276 -0
- package/lib/relations/belongsToMany/BelongsToManyUnrelateOperation.js +11 -0
- package/lib/relations/belongsToOne/BelongsToOneDeleteOperation.js +2 -8
- package/lib/relations/belongsToOne/BelongsToOneInsertOperation.js +6 -11
- package/lib/relations/belongsToOne/BelongsToOneRelateOperation.js +3 -9
- package/lib/relations/belongsToOne/BelongsToOneRelation.js +8 -19
- package/lib/relations/belongsToOne/BelongsToOneUnrelateOperation.js +53 -27
- package/lib/relations/hasMany/HasManyInsertOperation.js +5 -10
- package/lib/relations/hasMany/HasManyRelateOperation.js +5 -11
- package/lib/relations/hasMany/HasManyRelation.js +9 -11
- package/lib/relations/hasMany/HasManyUnrelateOperation.js +3 -9
- package/lib/relations/hasOne/HasOneRelation.js +2 -8
- package/lib/relations/hasOneThrough/HasOneThroughRelation.js +2 -8
- package/lib/relations/manyToMany/ManyToManyModifyMixin.js +10 -15
- package/lib/relations/manyToMany/ManyToManyMySqlModifyMixin.js +2 -8
- package/lib/relations/manyToMany/ManyToManyRelation.js +82 -50
- package/lib/relations/manyToMany/ManyToManySqliteModifyMixin.js +2 -8
- package/lib/relations/manyToMany/delete/ManyToManyDeleteOperation.js +5 -9
- package/lib/relations/manyToMany/delete/ManyToManyDeleteOperationBase.js +2 -8
- package/lib/relations/manyToMany/delete/ManyToManyDeleteSqliteOperation.js +3 -9
- package/lib/relations/manyToMany/find/ManyToManyFindOperation.js +20 -59
- package/lib/relations/manyToMany/insert/ManyToManyInsertOperation.js +6 -11
- package/lib/relations/manyToMany/relate/ManyToManyRelateOperation.js +4 -10
- package/lib/relations/manyToMany/unrelate/ManyToManyUnrelateMySqlOperation.js +3 -9
- package/lib/relations/manyToMany/unrelate/ManyToManyUnrelateOperation.js +5 -9
- package/lib/relations/manyToMany/unrelate/ManyToManyUnrelateOperationBase.js +3 -9
- package/lib/relations/manyToMany/unrelate/ManyToManyUnrelateSqliteOperation.js +3 -9
- package/lib/relations/manyToMany/update/ManyToManyUpdateMySqlOperation.js +3 -9
- package/lib/relations/manyToMany/update/ManyToManyUpdateOperation.js +5 -9
- package/lib/relations/manyToMany/update/ManyToManyUpdateOperationBase.js +64 -72
- package/lib/relations/manyToMany/update/ManyToManyUpdateSqliteOperation.js +3 -9
- package/lib/transaction.js +3 -9
- package/lib/utils/assert.js +3 -11
- package/lib/utils/buildUtils.js +2 -8
- package/lib/utils/classUtils.js +1 -7
- package/lib/utils/createModifier.js +2 -8
- package/lib/utils/identifierMapping.js +23 -27
- package/lib/utils/internalPropUtils.js +1 -7
- package/lib/utils/knexUtils.js +84 -40
- package/lib/utils/mixin.js +3 -10
- package/lib/utils/normalizeIds.js +2 -8
- package/lib/utils/objectUtils.js +41 -60
- package/lib/utils/parseFieldExpression.js +16 -16
- package/lib/utils/promiseUtils/after.js +2 -8
- package/lib/utils/promiseUtils/afterReturn.js +2 -8
- package/lib/utils/promiseUtils/index.js +7 -16
- package/lib/utils/promiseUtils/isPromise.js +2 -8
- package/lib/utils/promiseUtils/map.js +2 -8
- package/lib/utils/promiseUtils/mapAfterAllReturn.js +2 -8
- package/lib/utils/promiseUtils/try.js +2 -8
- package/lib/utils/resolveModel.js +10 -17
- package/lib/utils/tmpColumnUtils.js +2 -9
- package/lib/utils/warnings.js +2 -14
- package/package.json +25 -17
- package/typings/objection/index.d.ts +3217 -2179
- package/lib/.eslintrc.json +0 -8
- package/lib/model/graph/ModelGraph.js +0 -80
- package/lib/model/graph/ModelGraphBuilder.js +0 -241
- package/lib/model/graph/ModelGraphEdge.js +0 -52
- package/lib/model/graph/ModelGraphNode.js +0 -238
- package/lib/queryBuilder/graph/GraphAction.js +0 -69
- package/lib/queryBuilder/graph/GraphData.js +0 -53
- package/lib/queryBuilder/graph/GraphFetcher.js +0 -197
- package/lib/queryBuilder/graph/GraphNodeDbExistence.js +0 -184
- package/lib/queryBuilder/graph/GraphOperation.js +0 -27
- package/lib/queryBuilder/graph/delete/GraphDelete.js +0 -73
- package/lib/queryBuilder/graph/delete/GraphDeleteAction.js +0 -88
- package/lib/queryBuilder/graph/insert/DeferredRelationGraphInsertAction.js +0 -58
- package/lib/queryBuilder/graph/insert/GraphInsert.js +0 -296
- package/lib/queryBuilder/graph/insert/GraphInsertAction.js +0 -233
- package/lib/queryBuilder/graph/insert/JoinRowGraphInsertAction.js +0 -92
- package/lib/queryBuilder/graph/patch/GraphPatch.js +0 -20
- package/lib/queryBuilder/graph/patch/GraphPatchAction.js +0 -289
- package/lib/queryBuilder/graph/recursiveUpsert/GraphRecursiveUpsert.js +0 -31
- package/lib/queryBuilder/graph/recursiveUpsert/GraphRecursiveUpsertAction.js +0 -126
- package/lib/queryBuilder/operations/jsonApi/WhereJsonHasPostgresOperation.js +0 -30
- package/lib/queryBuilder/operations/jsonApi/WhereJsonNotObjectPostgresOperation.js +0 -37
- package/lib/queryBuilder/operations/jsonApi/WhereJsonPostgresOperation.js +0 -62
- package/lib/queryBuilder/operations/jsonApi/postgresJsonApi.js +0 -192
- package/lib/utils/wrapDbError.js +0 -64
|
@@ -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
|
+
}
|