objection 3.2.0 → 3.4.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +13 -13
- package/lib/model/AjvValidator.js +220 -13
- package/lib/model/Model.js +3 -1
- package/lib/model/modelId.js +10 -2
- package/lib/model/modelQueryProps.js +7 -0
- package/lib/queryBuilder/QueryBuilder.js +191 -8
- package/lib/queryBuilder/QueryBuilderOperationSupport.js +5 -0
- package/lib/queryBuilder/ReferenceBuilder.js +5 -3
- package/lib/queryBuilder/RelationExpression.js +13 -1
- package/lib/queryBuilder/StaticHookArguments.js +12 -1
- package/lib/queryBuilder/graph/GraphFetcher.js +1 -3
- package/lib/queryBuilder/graph/GraphOptions.js +30 -1
- package/lib/queryBuilder/graph/GraphUpsert.js +38 -1
- package/lib/queryBuilder/graph/insert/DeferredRelationGraphInsertAction.js +57 -0
- package/lib/queryBuilder/graph/insert/GraphInsert.js +107 -8
- package/lib/queryBuilder/graph/insert/GraphInsertAction.js +8 -1
- package/lib/queryBuilder/graph/patch/GraphPatchAction.js +8 -2
- package/lib/queryBuilder/join/JoinResultParser.js +22 -0
- package/lib/queryBuilder/join/RelationJoiner.js +80 -27
- package/lib/queryBuilder/join/utils.js +20 -2
- package/lib/queryBuilder/operations/InsertAndFetchOperation.js +7 -1
- package/lib/queryBuilder/operations/InsertGraphOperation.js +2 -2
- package/lib/queryBuilder/operations/InsertOperation.js +129 -13
- package/lib/queryBuilder/operations/QueryBuilderOperation.js +4 -0
- package/lib/queryBuilder/operations/ReturningOperation.js +28 -3
- package/lib/queryBuilder/operations/UpdateOperation.js +3 -2
- package/lib/queryBuilder/operations/UpsertGraphOperation.js +5 -0
- package/lib/queryBuilder/operations/WhereGroupOperation.js +41 -0
- package/lib/queryBuilder/operations/eager/EagerOperation.js +4 -0
- package/lib/queryBuilder/operations/eager/JoinEagerOperation.js +2 -6
- package/lib/queryBuilder/operations/eager/WhereInEagerOperation.js +10 -33
- package/lib/queryBuilder/operations/jsonApi/WhereJsonHasPostgresOperation.js +3 -3
- package/lib/queryBuilder/operations/jsonApi/WhereJsonNotObjectPostgresOperation.js +1 -1
- package/lib/queryBuilder/operations/jsonApi/postgresJsonApi.js +41 -34
- package/lib/queryBuilder/operations/select/Selection.js +95 -4
- package/lib/queryBuilder/operations/whereInComposite/WhereInCompositeMsSqlOperation.js +6 -0
- package/lib/queryBuilder/operations/whereInComposite/WhereInCompositeOperation.js +5 -1
- package/lib/queryBuilder/operations/whereInComposite/WhereInCompositeSqliteOperation.js +9 -0
- package/lib/queryBuilder/parsers/jsonFieldExpressionParser.js +81 -43
- package/lib/queryBuilder/parsers/jsonFieldExpressionParser.pegjs +5 -4
- package/lib/queryBuilder/whereGroups.js +125 -0
- package/lib/relations/Relation.js +5 -2
- package/lib/relations/RelationFindOperation.js +46 -2
- package/lib/relations/RelationProperty.js +3 -2
- package/lib/relations/hasMany/HasManyRelateOperation.js +5 -2
- package/lib/relations/hasMany/HasManyUnrelateOperation.js +5 -2
- package/lib/relations/manyToMany/ManyToManyModifyMixin.js +26 -3
- package/lib/relations/manyToMany/ManyToManyMySqlModifyMixin.js +66 -0
- package/lib/relations/manyToMany/ManyToManyPostgresModifyMixin.js +27 -0
- package/lib/relations/manyToMany/ManyToManyRelation.js +104 -51
- package/lib/relations/manyToMany/find/ManyToManyFindOperation.js +4 -2
- package/lib/relations/manyToMany/unrelate/ManyToManyUnrelateMySqlOperation.js +16 -0
- package/lib/relations/manyToMany/unrelate/ManyToManyUnrelateOperationBase.js +5 -1
- package/lib/relations/manyToMany/unrelate/ManyToManyUnrelatePostgresOperation.js +16 -0
- package/lib/relations/manyToMany/update/ManyToManyUpdateMySqlOperation.js +12 -0
- package/lib/relations/manyToMany/update/ManyToManyUpdateOperationBase.js +12 -2
- package/lib/relations/manyToMany/update/ManyToManyUpdatePostgresOperation.js +12 -0
- package/lib/utils/assert.js +9 -0
- package/lib/utils/deprecate.js +6 -0
- package/lib/utils/identifierMapping.js +15 -1
- package/lib/utils/parseFieldExpression.js +28 -0
- package/lib/utils/wrapDbError.js +36 -0
- package/package.json +8 -7
- package/typings/objection/index.d.ts +281 -29
|
@@ -1,6 +1,7 @@
|
|
|
1
1
|
'use strict';
|
|
2
2
|
|
|
3
3
|
const { JoinRowGraphInsertAction } = require('./JoinRowGraphInsertAction');
|
|
4
|
+
const { DeferredRelationGraphInsertAction } = require('./DeferredRelationGraphInsertAction');
|
|
4
5
|
const { GraphInsertAction } = require('./GraphInsertAction');
|
|
5
6
|
const { GraphOperation } = require('../GraphOperation');
|
|
6
7
|
const { ModelGraphEdge } = require('../../../model/graph/ModelGraphEdge');
|
|
@@ -9,10 +10,15 @@ class GraphInsert extends GraphOperation {
|
|
|
9
10
|
constructor(...args) {
|
|
10
11
|
super(...args);
|
|
11
12
|
this.dependencies = this._createDependencyMap();
|
|
13
|
+
this.deferredEdges = new Set();
|
|
12
14
|
}
|
|
13
15
|
|
|
14
16
|
createActions() {
|
|
15
|
-
return [
|
|
17
|
+
return [
|
|
18
|
+
...this._createNormalActions(),
|
|
19
|
+
...this._createDeferredRelationActions(),
|
|
20
|
+
...this._createJoinRowActions(),
|
|
21
|
+
];
|
|
16
22
|
}
|
|
17
23
|
|
|
18
24
|
_createDependencyMap() {
|
|
@@ -76,6 +82,10 @@ class GraphInsert extends GraphOperation {
|
|
|
76
82
|
});
|
|
77
83
|
|
|
78
84
|
if (nodesToInsert.length === 0) {
|
|
85
|
+
if (handledNodes.size !== this.graph.nodes.length && this._breakCycle(handledNodes)) {
|
|
86
|
+
continue;
|
|
87
|
+
}
|
|
88
|
+
|
|
79
89
|
break;
|
|
80
90
|
}
|
|
81
91
|
|
|
@@ -83,6 +93,7 @@ class GraphInsert extends GraphOperation {
|
|
|
83
93
|
new GraphInsertAction(this.graphData, {
|
|
84
94
|
nodes: nodesToInsert,
|
|
85
95
|
dependencies: this.dependencies,
|
|
96
|
+
deferredEdges: this.deferredEdges,
|
|
86
97
|
}),
|
|
87
98
|
);
|
|
88
99
|
|
|
@@ -98,19 +109,71 @@ class GraphInsert extends GraphOperation {
|
|
|
98
109
|
return actions;
|
|
99
110
|
}
|
|
100
111
|
|
|
101
|
-
|
|
102
|
-
|
|
112
|
+
// Tries to break a dependency cycle by deferring one `BelongsToOneRelation`
|
|
113
|
+
// edge that points to a `#ref` node. The owner node is then inserted without
|
|
114
|
+
// the foreign key, which is patched in by `DeferredRelationGraphInsertAction`
|
|
115
|
+
// once the referenced node has been inserted. Cycles can only exist through
|
|
116
|
+
// `#ref` nodes, since the graph is a tree otherwise.
|
|
117
|
+
_breakCycle(handledNodes) {
|
|
118
|
+
for (const node of this.graph.nodes) {
|
|
119
|
+
if (this._isHandled(node, handledNodes)) {
|
|
120
|
+
continue;
|
|
121
|
+
}
|
|
122
|
+
|
|
123
|
+
for (const edge of this._pendingDependencyEdges(node, handledNodes)) {
|
|
124
|
+
if (
|
|
125
|
+
this._canDeferEdge(edge, node) &&
|
|
126
|
+
this._isBlockedBy(edge.relatedNode, node, handledNodes, new Set())
|
|
127
|
+
) {
|
|
128
|
+
this.deferredEdges.add(edge);
|
|
129
|
+
return true;
|
|
130
|
+
}
|
|
131
|
+
}
|
|
132
|
+
}
|
|
133
|
+
|
|
134
|
+
return false;
|
|
103
135
|
}
|
|
104
136
|
|
|
105
|
-
|
|
106
|
-
|
|
137
|
+
_canDeferEdge(edge, node) {
|
|
138
|
+
return (
|
|
139
|
+
edge.type === ModelGraphEdge.Type.Relation &&
|
|
140
|
+
edge.relation.isObjectionBelongsToOneRelation &&
|
|
141
|
+
edge.isOwnerNode(node) &&
|
|
142
|
+
edge.relatedNode.isReference &&
|
|
143
|
+
// Both ends need to be inserted for the foreign key to be patched in later.
|
|
144
|
+
this.graphOptions.shouldInsert(node, this.graphData) &&
|
|
145
|
+
this.graphOptions.shouldInsert(edge.relatedNode.referencedNode, this.graphData) &&
|
|
146
|
+
!this._isReadByPropertyReference(node, edge.relation.ownerProp)
|
|
147
|
+
);
|
|
148
|
+
}
|
|
149
|
+
|
|
150
|
+
// Returns true if a `#ref{}` property reference reads one of the `props` of
|
|
151
|
+
// `node`. Such references may be resolved before a deferred prop is set,
|
|
152
|
+
// which would resolve them to `undefined`.
|
|
153
|
+
_isReadByPropertyReference(node, props) {
|
|
154
|
+
return node.refEdges.some((edge) => {
|
|
155
|
+
return (
|
|
156
|
+
edge.refType === ModelGraphEdge.ReferenceType.Property &&
|
|
157
|
+
edge.isRelatedNode(node) &&
|
|
158
|
+
props.props.includes(edge.refRelatedDataPath[0])
|
|
159
|
+
);
|
|
160
|
+
});
|
|
161
|
+
}
|
|
162
|
+
|
|
163
|
+
// Returns true if `node` (transitively) waits for `blockingNode` to be handled.
|
|
164
|
+
_isBlockedBy(node, blockingNode, handledNodes, visitedNodes) {
|
|
165
|
+
if (node === blockingNode) {
|
|
166
|
+
return true;
|
|
167
|
+
}
|
|
168
|
+
|
|
169
|
+
if (visitedNodes.has(node)) {
|
|
107
170
|
return false;
|
|
108
171
|
}
|
|
109
172
|
|
|
110
|
-
|
|
111
|
-
const dependencyNode = edge.getOtherNode(node);
|
|
173
|
+
visitedNodes.add(node);
|
|
112
174
|
|
|
113
|
-
|
|
175
|
+
for (const edge of this._pendingDependencyEdges(node, handledNodes)) {
|
|
176
|
+
if (this._isBlockedBy(edge.getOtherNode(node), blockingNode, handledNodes, visitedNodes)) {
|
|
114
177
|
return true;
|
|
115
178
|
}
|
|
116
179
|
}
|
|
@@ -118,6 +181,42 @@ class GraphInsert extends GraphOperation {
|
|
|
118
181
|
return false;
|
|
119
182
|
}
|
|
120
183
|
|
|
184
|
+
_createDeferredRelationActions() {
|
|
185
|
+
if (this.deferredEdges.size === 0) {
|
|
186
|
+
return [];
|
|
187
|
+
}
|
|
188
|
+
|
|
189
|
+
return [
|
|
190
|
+
new DeferredRelationGraphInsertAction(this.graphData, {
|
|
191
|
+
edges: [...this.deferredEdges],
|
|
192
|
+
}),
|
|
193
|
+
];
|
|
194
|
+
}
|
|
195
|
+
|
|
196
|
+
_isHandled(node, handledNodes) {
|
|
197
|
+
return handledNodes.has(node);
|
|
198
|
+
}
|
|
199
|
+
|
|
200
|
+
_hasDependencies(node, handledNodes) {
|
|
201
|
+
return this._pendingDependencyEdges(node, handledNodes).length > 0;
|
|
202
|
+
}
|
|
203
|
+
|
|
204
|
+
// Returns the dependency edges of `node` whose other node still needs to be
|
|
205
|
+
// inserted first. Deferred edges are ignored.
|
|
206
|
+
_pendingDependencyEdges(node, handledNodes) {
|
|
207
|
+
const edges = this.dependencies.get(node) || [];
|
|
208
|
+
|
|
209
|
+
return edges.filter((edge) => {
|
|
210
|
+
const dependencyNode = edge.getOtherNode(node);
|
|
211
|
+
|
|
212
|
+
return (
|
|
213
|
+
!this.deferredEdges.has(edge) &&
|
|
214
|
+
!handledNodes.has(dependencyNode) &&
|
|
215
|
+
!this.currentGraph.nodeForNode(dependencyNode)
|
|
216
|
+
);
|
|
217
|
+
});
|
|
218
|
+
}
|
|
219
|
+
|
|
121
220
|
_markHandled(node, handledNodes) {
|
|
122
221
|
handledNodes.add(node);
|
|
123
222
|
|
|
@@ -14,12 +14,15 @@ const promiseUtils = require('../../../utils/promiseUtils');
|
|
|
14
14
|
* when needed.
|
|
15
15
|
*/
|
|
16
16
|
class GraphInsertAction extends GraphAction {
|
|
17
|
-
constructor(graphData, { nodes, dependencies }) {
|
|
17
|
+
constructor(graphData, { nodes, dependencies, deferredEdges }) {
|
|
18
18
|
super(graphData);
|
|
19
19
|
|
|
20
20
|
// Nodes to insert.
|
|
21
21
|
this.nodes = nodes;
|
|
22
22
|
this.dependencies = dependencies;
|
|
23
|
+
// Dependency edges that are resolved once the dependency node has been
|
|
24
|
+
// inserted, and are patched in by `DeferredRelationGraphInsertAction`.
|
|
25
|
+
this.deferredEdges = deferredEdges;
|
|
23
26
|
}
|
|
24
27
|
|
|
25
28
|
run(builder) {
|
|
@@ -63,6 +66,10 @@ class GraphInsertAction extends GraphAction {
|
|
|
63
66
|
|
|
64
67
|
if (edges) {
|
|
65
68
|
for (const edge of edges) {
|
|
69
|
+
if (this.deferredEdges.has(edge)) {
|
|
70
|
+
continue;
|
|
71
|
+
}
|
|
72
|
+
|
|
66
73
|
// `node` needs `dependencyNode` to have been inserted (and it has been).
|
|
67
74
|
const dependencyNode = edge.getOtherNode(node);
|
|
68
75
|
|
|
@@ -3,7 +3,7 @@
|
|
|
3
3
|
const { getModel } = require('../../../model/getModel');
|
|
4
4
|
const { GraphAction } = require('../GraphAction');
|
|
5
5
|
const { isInternalProp } = require('../../../utils/internalPropUtils');
|
|
6
|
-
const { difference, isObject, jsonEquals } = require('../../../utils/objectUtils');
|
|
6
|
+
const { difference, isObject, isPlainObject, jsonEquals } = require('../../../utils/objectUtils');
|
|
7
7
|
const promiseUtils = require('../../../utils/promiseUtils');
|
|
8
8
|
|
|
9
9
|
class GraphPatchAction extends GraphAction {
|
|
@@ -251,13 +251,19 @@ function childQueryOptions() {
|
|
|
251
251
|
}
|
|
252
252
|
|
|
253
253
|
function nonStrictEquals(val1, val2) {
|
|
254
|
-
|
|
254
|
+
// Only use loose equality for non-JSON values, so that for example `false`
|
|
255
|
+
// and `[]` are not considered equal. See issue #1874.
|
|
256
|
+
if (!isJsonValue(val1) && !isJsonValue(val2) && val1 == val2) {
|
|
255
257
|
return true;
|
|
256
258
|
}
|
|
257
259
|
|
|
258
260
|
return jsonEquals(val1, val2);
|
|
259
261
|
}
|
|
260
262
|
|
|
263
|
+
function isJsonValue(value) {
|
|
264
|
+
return Array.isArray(value) || isPlainObject(value);
|
|
265
|
+
}
|
|
266
|
+
|
|
261
267
|
function isToBeUnrelatedOrDeleted(node) {
|
|
262
268
|
return !!node && node.toBeUnrelatedOrDeleted;
|
|
263
269
|
}
|
|
@@ -22,6 +22,8 @@ class JoinResultParser {
|
|
|
22
22
|
return flatRows;
|
|
23
23
|
}
|
|
24
24
|
|
|
25
|
+
this._checkIdColumns(flatRows[0]);
|
|
26
|
+
|
|
25
27
|
this.columnsByTableNode = this._createColumns(flatRows[0]);
|
|
26
28
|
this.parentMap = new Map();
|
|
27
29
|
this.rootModels = [];
|
|
@@ -93,6 +95,26 @@ class JoinResultParser {
|
|
|
93
95
|
}
|
|
94
96
|
}
|
|
95
97
|
|
|
98
|
+
_checkIdColumns(row) {
|
|
99
|
+
for (const tableNode of this.tableTree.nodes) {
|
|
100
|
+
const idColumns = tableNode.modelClass.getIdColumnArray();
|
|
101
|
+
const hasIdColumns =
|
|
102
|
+
idColumns.length > 0 &&
|
|
103
|
+
idColumns.every((column) =>
|
|
104
|
+
Object.prototype.hasOwnProperty.call(row, tableNode.getColumnAliasForColumn(column)),
|
|
105
|
+
);
|
|
106
|
+
|
|
107
|
+
if (!hasIdColumns) {
|
|
108
|
+
throw new Error(
|
|
109
|
+
`withGraphJoined: the id column(s) [${idColumns.join(', ')}] of model ${tableNode.modelClass.name} ` +
|
|
110
|
+
`are missing from the result, so the joined rows can't be grouped into models. ` +
|
|
111
|
+
`withGraphJoined requires all models in the relation expression to have a primary key ` +
|
|
112
|
+
`(idColumn) that exists in the table. Use withGraphFetched for models without one.`,
|
|
113
|
+
);
|
|
114
|
+
}
|
|
115
|
+
}
|
|
116
|
+
}
|
|
117
|
+
|
|
96
118
|
_createColumns(row) {
|
|
97
119
|
const columns = Object.keys(row).map((columnAlias) =>
|
|
98
120
|
JoinResultColumn.create({ tableTree: this.tableTree, columnAlias }),
|
|
@@ -8,7 +8,7 @@ const { ValidationErrorType } = require('../../model/ValidationError');
|
|
|
8
8
|
|
|
9
9
|
const { TableTree } = require('./TableTree');
|
|
10
10
|
const { JoinResultParser } = require('./JoinResultParser');
|
|
11
|
-
const {
|
|
11
|
+
const { getIdentifierLengthLimit } = require('./utils');
|
|
12
12
|
|
|
13
13
|
/**
|
|
14
14
|
* Given a relation expression, builds a query using joins to fetch it.
|
|
@@ -24,6 +24,8 @@ class RelationJoiner {
|
|
|
24
24
|
this.modifiers = null;
|
|
25
25
|
|
|
26
26
|
this.options = defaultOptions();
|
|
27
|
+
// The `joinOperation` options of the individual `withGraphJoined` calls.
|
|
28
|
+
this.joinOperations = [];
|
|
27
29
|
this.tableTree = null;
|
|
28
30
|
this.internalSelections = null;
|
|
29
31
|
}
|
|
@@ -49,6 +51,11 @@ class RelationJoiner {
|
|
|
49
51
|
return this;
|
|
50
52
|
}
|
|
51
53
|
|
|
54
|
+
setJoinOperations(joinOperations) {
|
|
55
|
+
this.joinOperations = joinOperations;
|
|
56
|
+
return this;
|
|
57
|
+
}
|
|
58
|
+
|
|
52
59
|
/**
|
|
53
60
|
* Fetches the column information needed for building the select clauses.
|
|
54
61
|
*
|
|
@@ -81,23 +88,6 @@ class RelationJoiner {
|
|
|
81
88
|
}
|
|
82
89
|
}
|
|
83
90
|
|
|
84
|
-
releaseInternalRootSelection(column) {
|
|
85
|
-
const rootTableNode = this.tableTree && this.tableTree.rootNode;
|
|
86
|
-
|
|
87
|
-
if (!rootTableNode || !this.internalSelections) {
|
|
88
|
-
return false;
|
|
89
|
-
}
|
|
90
|
-
|
|
91
|
-
const alias = rootTableNode.getColumnAliasForColumn(column);
|
|
92
|
-
const count = this.internalSelections.length;
|
|
93
|
-
|
|
94
|
-
this.internalSelections = this.internalSelections.filter(
|
|
95
|
-
(it) => it.table !== rootTableNode.alias || it.alias !== alias,
|
|
96
|
-
);
|
|
97
|
-
|
|
98
|
-
return this.internalSelections.length !== count;
|
|
99
|
-
}
|
|
100
|
-
|
|
101
91
|
parseResult(builder, flatRows) {
|
|
102
92
|
const parser = JoinResultParser.create({
|
|
103
93
|
tableTree: this._getTableTree(builder),
|
|
@@ -134,12 +124,18 @@ class RelationJoiner {
|
|
|
134
124
|
// also contain selects.
|
|
135
125
|
userSelectQuery.modify(tableNode.relation.modify);
|
|
136
126
|
|
|
127
|
+
if (selectsOnlyAliasesKeepingSelectAll(userSelectQuery.findAllSelections())) {
|
|
128
|
+
// All columns are selected from the joined subquery in the main query,
|
|
129
|
+
// so the subquery needs to select them too.
|
|
130
|
+
subqueryToJoin.select(`${subqueryToJoin.tableRef()}.*`);
|
|
131
|
+
}
|
|
132
|
+
|
|
137
133
|
// Save the query that contains the user specified selects
|
|
138
134
|
// for later use.
|
|
139
135
|
userSelectQueries.set(tableNode, userSelectQuery);
|
|
140
136
|
|
|
141
137
|
tableNode.relation.join(builder, {
|
|
142
|
-
joinOperation: this.
|
|
138
|
+
joinOperation: this._getJoinOperation(tableNode),
|
|
143
139
|
|
|
144
140
|
ownerTable: tableNode.parentNode.alias,
|
|
145
141
|
relatedTableAlias: tableNode.alias,
|
|
@@ -156,6 +152,26 @@ class RelationJoiner {
|
|
|
156
152
|
}
|
|
157
153
|
}
|
|
158
154
|
|
|
155
|
+
// Returns the `joinOperation` of the last `withGraphJoined` call that
|
|
156
|
+
// contains the relation of `tableNode`, falling back to the default.
|
|
157
|
+
_getJoinOperation(tableNode) {
|
|
158
|
+
const path = [];
|
|
159
|
+
|
|
160
|
+
for (let node = tableNode; node.parentNode; node = node.parentNode) {
|
|
161
|
+
path.unshift(node.relationProperty);
|
|
162
|
+
}
|
|
163
|
+
|
|
164
|
+
for (let i = this.joinOperations.length - 1; i >= 0; --i) {
|
|
165
|
+
const { expression, joinOperation } = this.joinOperations[i];
|
|
166
|
+
|
|
167
|
+
if (containsPath(expression, path)) {
|
|
168
|
+
return joinOperation;
|
|
169
|
+
}
|
|
170
|
+
}
|
|
171
|
+
|
|
172
|
+
return this.options.joinOperation;
|
|
173
|
+
}
|
|
174
|
+
|
|
159
175
|
_buildSelects({ builder, tableNode, userSelectQueries }) {
|
|
160
176
|
const { selections, internalSelections } = getSelectionsForNode({
|
|
161
177
|
builder,
|
|
@@ -163,14 +179,28 @@ class RelationJoiner {
|
|
|
163
179
|
userSelectQueries,
|
|
164
180
|
});
|
|
165
181
|
|
|
182
|
+
const lengthLimit = getIdentifierLengthLimit(builder.knex());
|
|
183
|
+
|
|
166
184
|
for (const selection of selections) {
|
|
167
|
-
checkAliasLength(tableNode.modelClass, selection.name);
|
|
185
|
+
checkAliasLength(tableNode.modelClass, selection.name, lengthLimit);
|
|
168
186
|
}
|
|
169
187
|
|
|
170
188
|
// Save the selections that were added internally (not by the user)
|
|
171
|
-
// so that we can later remove the corresponding properties
|
|
172
|
-
//
|
|
173
|
-
|
|
189
|
+
// so that we can later remove the corresponding properties. The root
|
|
190
|
+
// ones are registered with the builder, which removes them from the
|
|
191
|
+
// result models after all eager operations have run, as they may still
|
|
192
|
+
// need them. The nested ones are removed when parsing the result.
|
|
193
|
+
this.internalSelections = [];
|
|
194
|
+
|
|
195
|
+
for (const selection of internalSelections) {
|
|
196
|
+
if (selection.table === tableNode.alias) {
|
|
197
|
+
builder.addInternalSelection(
|
|
198
|
+
tableNode.modelClass.columnNameToPropertyName(selection.column),
|
|
199
|
+
);
|
|
200
|
+
} else {
|
|
201
|
+
this.internalSelections.push(selection);
|
|
202
|
+
}
|
|
203
|
+
}
|
|
174
204
|
|
|
175
205
|
builder.select(selectionsToStrings(selections));
|
|
176
206
|
}
|
|
@@ -185,9 +215,21 @@ function defaultOptions() {
|
|
|
185
215
|
};
|
|
186
216
|
}
|
|
187
217
|
|
|
218
|
+
function containsPath(expression, path) {
|
|
219
|
+
for (const name of path) {
|
|
220
|
+
expression = expression.childExpression(name);
|
|
221
|
+
|
|
222
|
+
if (!expression) {
|
|
223
|
+
return false;
|
|
224
|
+
}
|
|
225
|
+
}
|
|
226
|
+
|
|
227
|
+
return true;
|
|
228
|
+
}
|
|
229
|
+
|
|
188
230
|
function createSubqueryToJoin({ builder, tableNode, modifiers }) {
|
|
189
231
|
const { relation, expression, modelClass } = tableNode;
|
|
190
|
-
const modifierQuery = modelClass.query().childQueryOf(builder);
|
|
232
|
+
const modifierQuery = modelClass.query().childQueryOf(builder, { isJoinChildQuery: true });
|
|
191
233
|
|
|
192
234
|
for (const modifierName of expression.node.$modify) {
|
|
193
235
|
const modifier = createModifier({
|
|
@@ -341,14 +383,25 @@ function isNotSelectAll(selection) {
|
|
|
341
383
|
}
|
|
342
384
|
|
|
343
385
|
function isSelectAllSelectionSet(selections) {
|
|
386
|
+
selections = selections.filter(isNotKeepingSelectAll);
|
|
344
387
|
return selections.length === 0 || selections.some(isSelectAll);
|
|
345
388
|
}
|
|
346
389
|
|
|
347
|
-
function
|
|
348
|
-
|
|
390
|
+
function isNotKeepingSelectAll(selection) {
|
|
391
|
+
return !selection.keepsSelectAll;
|
|
392
|
+
}
|
|
393
|
+
|
|
394
|
+
function selectsOnlyAliasesKeepingSelectAll(selections) {
|
|
395
|
+
return selections.length > 0 && selections.every((it) => it.keepsSelectAll);
|
|
396
|
+
}
|
|
397
|
+
|
|
398
|
+
function checkAliasLength(modelClass, alias, lengthLimit) {
|
|
399
|
+
if (alias.length > lengthLimit) {
|
|
349
400
|
throw modelClass.createValidationError({
|
|
350
401
|
type: ValidationErrorType.RelationExpression,
|
|
351
|
-
message:
|
|
402
|
+
message:
|
|
403
|
+
`identifier ${alias} is over ${lengthLimit} characters long and would be truncated by the database engine. ` +
|
|
404
|
+
'Use the `minimize` option of withGraphJoined() to shorten the aliases.',
|
|
352
405
|
});
|
|
353
406
|
}
|
|
354
407
|
}
|
|
@@ -1,10 +1,27 @@
|
|
|
1
1
|
'use strict';
|
|
2
2
|
|
|
3
3
|
const { ValidationErrorType } = require('../../model/ValidationError');
|
|
4
|
+
const { isPostgres, isMySql, isMsSql, isSqlite } = require('../../utils/knexUtils');
|
|
4
5
|
|
|
5
|
-
const ID_LENGTH_LIMIT = 63;
|
|
6
6
|
const RELATION_RECURSION_LIMIT = 64;
|
|
7
7
|
|
|
8
|
+
// The maximum length of the column aliases of the joined relations, after
|
|
9
|
+
// which the database would truncate them. Unknown databases use the strictest
|
|
10
|
+
// of the known limits, the one of postgres.
|
|
11
|
+
function getIdentifierLengthLimit(knex) {
|
|
12
|
+
if (isPostgres(knex)) {
|
|
13
|
+
return 63;
|
|
14
|
+
} else if (isMySql(knex)) {
|
|
15
|
+
return 256;
|
|
16
|
+
} else if (isMsSql(knex)) {
|
|
17
|
+
return 128;
|
|
18
|
+
} else if (isSqlite(knex)) {
|
|
19
|
+
return Infinity;
|
|
20
|
+
} else {
|
|
21
|
+
return 63;
|
|
22
|
+
}
|
|
23
|
+
}
|
|
24
|
+
|
|
8
25
|
// Given a relation expression, goes through all first level children.
|
|
9
26
|
function forEachChildExpression(expr, modelClass, callback) {
|
|
10
27
|
if (expr.node.$allRecursive || expr.maxRecursionDepth > RELATION_RECURSION_LIMIT) {
|
|
@@ -18,8 +35,9 @@ function forEachChildExpression(expr, modelClass, callback) {
|
|
|
18
35
|
}
|
|
19
36
|
|
|
20
37
|
module.exports = {
|
|
21
|
-
ID_LENGTH_LIMIT,
|
|
22
38
|
RELATION_RECURSION_LIMIT,
|
|
23
39
|
|
|
40
|
+
getIdentifierLengthLimit,
|
|
41
|
+
|
|
24
42
|
forEachChildExpression,
|
|
25
43
|
};
|
|
@@ -22,10 +22,16 @@ class InsertAndFetchOperation extends DelegateOperation {
|
|
|
22
22
|
const modelClass = builder.modelClass();
|
|
23
23
|
const insertedModels = await super.onAfter2(builder, inserted);
|
|
24
24
|
|
|
25
|
-
|
|
25
|
+
// Models without identifiers can't be fetched. This happens when a row
|
|
26
|
+
// with a database generated identifier is ignored by `onConflict()`.
|
|
27
|
+
const insertedModelArray = asArray(insertedModels).filter((model) => model && model.$hasId());
|
|
26
28
|
const idProps = modelClass.getIdPropertyArray();
|
|
27
29
|
const ids = insertedModelArray.map((model) => model.$id());
|
|
28
30
|
|
|
31
|
+
if (ids.length === 0) {
|
|
32
|
+
return insertedModels;
|
|
33
|
+
}
|
|
34
|
+
|
|
29
35
|
const fetchedModels = await modelClass
|
|
30
36
|
.query()
|
|
31
37
|
.childQueryOf(builder)
|