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