@abedmurrar/objection 0.0.0-stage → 4.0.0-alpha.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/LICENSE +23 -0
- package/README.md +69 -2
- package/lib/.eslintrc.json +8 -0
- package/lib/initialize.js +14 -0
- package/lib/model/AjvValidator.js +325 -0
- package/lib/model/Model.js +942 -0
- package/lib/model/ModifierNotFoundError.js +12 -0
- package/lib/model/NotFoundError.js +23 -0
- package/lib/model/RelationDoesNotExistError.js +14 -0
- package/lib/model/ValidationError.js +54 -0
- package/lib/model/Validator.js +26 -0
- package/lib/model/getModel.js +9 -0
- package/lib/model/graph/ModelGraph.js +80 -0
- package/lib/model/graph/ModelGraphBuilder.js +241 -0
- package/lib/model/graph/ModelGraphEdge.js +52 -0
- package/lib/model/graph/ModelGraphNode.js +240 -0
- package/lib/model/inheritModel.js +28 -0
- package/lib/model/modelBindKnex.js +83 -0
- package/lib/model/modelClone.js +121 -0
- package/lib/model/modelColPropMap.js +34 -0
- package/lib/model/modelId.js +62 -0
- package/lib/model/modelJsonAttributes.js +92 -0
- package/lib/model/modelParseRelations.js +82 -0
- package/lib/model/modelQueryProps.js +147 -0
- package/lib/model/modelSet.js +136 -0
- package/lib/model/modelTableMetadata.js +83 -0
- package/lib/model/modelToJson.js +187 -0
- package/lib/model/modelUtils.js +47 -0
- package/lib/model/modelValidate.js +48 -0
- package/lib/model/modelValues.js +126 -0
- package/lib/model/modelVisitor.js +41 -0
- package/lib/objection.js +114 -0
- package/lib/queryBuilder/FunctionBuilder.js +339 -0
- package/lib/queryBuilder/InternalOptions.js +29 -0
- package/lib/queryBuilder/JoinBuilder.js +157 -0
- package/lib/queryBuilder/QueryBuilder.js +1398 -0
- package/lib/queryBuilder/QueryBuilderBase.js +884 -0
- package/lib/queryBuilder/QueryBuilderContext.js +27 -0
- package/lib/queryBuilder/QueryBuilderContextBase.js +35 -0
- package/lib/queryBuilder/QueryBuilderOperationSupport.js +558 -0
- package/lib/queryBuilder/QueryBuilderUserContext.js +31 -0
- package/lib/queryBuilder/RawBuilder.js +93 -0
- package/lib/queryBuilder/ReferenceBuilder.js +284 -0
- package/lib/queryBuilder/RelationExpression.js +431 -0
- package/lib/queryBuilder/StaticHookArguments.js +134 -0
- package/lib/queryBuilder/ValueBuilder.js +127 -0
- package/lib/queryBuilder/expressions.js +151 -0
- package/lib/queryBuilder/functions/mysql.js +817 -0
- package/lib/queryBuilder/graph/GraphAction.js +69 -0
- package/lib/queryBuilder/graph/GraphData.js +14 -0
- package/lib/queryBuilder/graph/GraphFetcher.js +191 -0
- package/lib/queryBuilder/graph/GraphNodeDbExistence.js +184 -0
- package/lib/queryBuilder/graph/GraphOperation.js +27 -0
- package/lib/queryBuilder/graph/GraphOptions.js +207 -0
- package/lib/queryBuilder/graph/GraphUpsert.js +305 -0
- package/lib/queryBuilder/graph/delete/GraphDelete.js +20 -0
- package/lib/queryBuilder/graph/delete/GraphDeleteAction.js +74 -0
- package/lib/queryBuilder/graph/insert/GraphInsert.js +145 -0
- package/lib/queryBuilder/graph/insert/GraphInsertAction.js +225 -0
- package/lib/queryBuilder/graph/insert/JoinRowGraphInsertAction.js +92 -0
- package/lib/queryBuilder/graph/patch/GraphPatch.js +20 -0
- package/lib/queryBuilder/graph/patch/GraphPatchAction.js +258 -0
- package/lib/queryBuilder/graph/recursiveUpsert/GraphRecursiveUpsert.js +31 -0
- package/lib/queryBuilder/graph/recursiveUpsert/GraphRecursiveUpsertAction.js +126 -0
- package/lib/queryBuilder/join/JoinResultColumn.js +23 -0
- package/lib/queryBuilder/join/JoinResultParser.js +119 -0
- package/lib/queryBuilder/join/RelationJoiner.js +341 -0
- package/lib/queryBuilder/join/TableNode.js +163 -0
- package/lib/queryBuilder/join/TableTree.js +85 -0
- package/lib/queryBuilder/join/utils.js +25 -0
- package/lib/queryBuilder/operations/DelegateOperation.js +141 -0
- package/lib/queryBuilder/operations/DeleteOperation.js +51 -0
- package/lib/queryBuilder/operations/FindByIdOperation.js +37 -0
- package/lib/queryBuilder/operations/FindByIdsOperation.js +29 -0
- package/lib/queryBuilder/operations/FindOperation.js +164 -0
- package/lib/queryBuilder/operations/FirstOperation.js +27 -0
- package/lib/queryBuilder/operations/FromOperation.js +110 -0
- package/lib/queryBuilder/operations/InsertAndFetchOperation.js +49 -0
- package/lib/queryBuilder/operations/InsertGraphAndFetchOperation.js +45 -0
- package/lib/queryBuilder/operations/InsertGraphOperation.js +108 -0
- package/lib/queryBuilder/operations/InsertOperation.js +206 -0
- package/lib/queryBuilder/operations/InstanceDeleteOperation.js +46 -0
- package/lib/queryBuilder/operations/InstanceFindOperation.js +21 -0
- package/lib/queryBuilder/operations/InstanceInsertOperation.js +24 -0
- package/lib/queryBuilder/operations/InstanceUpdateOperation.js +59 -0
- package/lib/queryBuilder/operations/JoinRelatedOperation.js +103 -0
- package/lib/queryBuilder/operations/KnexOperation.js +14 -0
- package/lib/queryBuilder/operations/MergeOperation.js +56 -0
- package/lib/queryBuilder/operations/ObjectionToKnexConvertingOperation.js +192 -0
- package/lib/queryBuilder/operations/OnBuildKnexOperation.js +29 -0
- package/lib/queryBuilder/operations/OnBuildOperation.js +29 -0
- package/lib/queryBuilder/operations/OnErrorOperation.js +29 -0
- package/lib/queryBuilder/operations/QueryBuilderOperation.js +297 -0
- package/lib/queryBuilder/operations/RangeOperation.js +47 -0
- package/lib/queryBuilder/operations/RelateOperation.js +27 -0
- package/lib/queryBuilder/operations/ReturningOperation.js +30 -0
- package/lib/queryBuilder/operations/RunAfterOperation.js +29 -0
- package/lib/queryBuilder/operations/RunBeforeOperation.js +29 -0
- package/lib/queryBuilder/operations/UnrelateOperation.js +23 -0
- package/lib/queryBuilder/operations/UpdateAndFetchOperation.js +80 -0
- package/lib/queryBuilder/operations/UpdateOperation.js +155 -0
- package/lib/queryBuilder/operations/UpsertGraphAndFetchOperation.js +45 -0
- package/lib/queryBuilder/operations/UpsertGraphOperation.js +66 -0
- package/lib/queryBuilder/operations/WhereCompositeOperation.js +57 -0
- package/lib/queryBuilder/operations/WhereInOperation.js +48 -0
- package/lib/queryBuilder/operations/eager/EagerOperation.js +61 -0
- package/lib/queryBuilder/operations/eager/JoinEagerOperation.js +51 -0
- package/lib/queryBuilder/operations/eager/NaiveEagerOperation.js +13 -0
- package/lib/queryBuilder/operations/eager/WhereInEagerOperation.js +285 -0
- package/lib/queryBuilder/operations/jsonApi/WhereJsonHasOperation.js +36 -0
- package/lib/queryBuilder/operations/jsonApi/WhereJsonNotObjectOperation.js +53 -0
- package/lib/queryBuilder/operations/jsonApi/WhereJsonOperation.js +31 -0
- package/lib/queryBuilder/operations/jsonApi/WhereJsonOverlapsOperation.js +31 -0
- package/lib/queryBuilder/operations/jsonApi/mysqlJsonApi.js +167 -0
- package/lib/queryBuilder/operations/jsonApi/postgresJsonApi.js +164 -0
- package/lib/queryBuilder/operations/select/SelectOperation.js +66 -0
- package/lib/queryBuilder/operations/select/Selection.js +123 -0
- package/lib/queryBuilder/operations/whereInComposite/WhereInCompositeMsSqlOperation.js +117 -0
- package/lib/queryBuilder/operations/whereInComposite/WhereInCompositeOperation.js +100 -0
- package/lib/queryBuilder/operations/whereInComposite/WhereInCompositeSqliteOperation.js +104 -0
- package/lib/queryBuilder/parsers/jsonFieldExpressionParser.js +886 -0
- package/lib/queryBuilder/parsers/jsonFieldExpressionParser.pegjs +97 -0
- package/lib/queryBuilder/parsers/relationExpressionParser.js +1042 -0
- package/lib/queryBuilder/parsers/relationExpressionParser.pegjs +141 -0
- package/lib/queryBuilder/transformations/CompositeQueryTransformation.js +22 -0
- package/lib/queryBuilder/transformations/QueryTransformation.js +11 -0
- package/lib/queryBuilder/transformations/WrapMysqlModifySubqueryTransformation.js +71 -0
- package/lib/queryBuilder/transformations/index.js +15 -0
- package/lib/queryBuilder/whereScopes.js +227 -0
- package/lib/relations/Relation.js +401 -0
- package/lib/relations/RelationDeleteOperation.js +29 -0
- package/lib/relations/RelationFindOperation.js +149 -0
- package/lib/relations/RelationInsertOperation.js +33 -0
- package/lib/relations/RelationOwner.js +236 -0
- package/lib/relations/RelationProperty.js +247 -0
- package/lib/relations/RelationUpdateOperation.js +30 -0
- package/lib/relations/belongsToOne/BelongsToOneDeleteOperation.js +23 -0
- package/lib/relations/belongsToOne/BelongsToOneInsertOperation.js +44 -0
- package/lib/relations/belongsToOne/BelongsToOneRelateOperation.js +50 -0
- package/lib/relations/belongsToOne/BelongsToOneRelation.js +53 -0
- package/lib/relations/belongsToOne/BelongsToOneUnrelateOperation.js +61 -0
- package/lib/relations/hasMany/HasManyInsertOperation.js +61 -0
- package/lib/relations/hasMany/HasManyRelateOperation.js +64 -0
- package/lib/relations/hasMany/HasManyRelation.js +49 -0
- package/lib/relations/hasMany/HasManyUnrelateOperation.js +33 -0
- package/lib/relations/hasOne/HasOneRelation.js +13 -0
- package/lib/relations/hasOneThrough/HasOneThroughRelation.js +13 -0
- package/lib/relations/manyToMany/ManyToManyModifyMixin.js +105 -0
- package/lib/relations/manyToMany/ManyToManyRelation.js +507 -0
- package/lib/relations/manyToMany/ManyToManySqliteModifyMixin.js +36 -0
- package/lib/relations/manyToMany/delete/ManyToManyDeleteOperation.js +10 -0
- package/lib/relations/manyToMany/delete/ManyToManyDeleteOperationBase.js +26 -0
- package/lib/relations/manyToMany/delete/ManyToManyDeleteSqliteOperation.js +12 -0
- package/lib/relations/manyToMany/find/ManyToManyFindOperation.js +105 -0
- package/lib/relations/manyToMany/insert/ManyToManyInsertOperation.js +52 -0
- package/lib/relations/manyToMany/relate/ManyToManyRelateOperation.js +81 -0
- package/lib/relations/manyToMany/unrelate/ManyToManyUnrelateOperation.js +14 -0
- package/lib/relations/manyToMany/unrelate/ManyToManyUnrelateOperationBase.js +34 -0
- package/lib/relations/manyToMany/unrelate/ManyToManyUnrelateSqliteOperation.js +16 -0
- package/lib/relations/manyToMany/update/ManyToManyUpdateOperation.js +10 -0
- package/lib/relations/manyToMany/update/ManyToManyUpdateOperationBase.js +82 -0
- package/lib/relations/manyToMany/update/ManyToManyUpdateSqliteOperation.js +12 -0
- package/lib/transaction.js +96 -0
- package/lib/utils/assert.js +25 -0
- package/lib/utils/buildUtils.js +21 -0
- package/lib/utils/classUtils.js +13 -0
- package/lib/utils/clone.js +2329 -0
- package/lib/utils/createModifier.js +42 -0
- package/lib/utils/deprecate.js +15 -0
- package/lib/utils/identifierMapping.js +227 -0
- package/lib/utils/internalPropUtils.js +11 -0
- package/lib/utils/knexUtils.js +76 -0
- package/lib/utils/mixin.js +25 -0
- package/lib/utils/mysqlJsonPath.js +55 -0
- package/lib/utils/normalizeIds.js +144 -0
- package/lib/utils/objectUtils.js +416 -0
- package/lib/utils/parseFieldExpression.js +51 -0
- package/lib/utils/postgresJsonPath.js +110 -0
- package/lib/utils/promiseUtils/after.js +17 -0
- package/lib/utils/promiseUtils/afterReturn.js +17 -0
- package/lib/utils/promiseUtils/index.js +17 -0
- package/lib/utils/promiseUtils/isPromise.js +11 -0
- package/lib/utils/promiseUtils/map.js +110 -0
- package/lib/utils/promiseUtils/mapAfterAllReturn.js +29 -0
- package/lib/utils/promiseUtils/try.js +22 -0
- package/lib/utils/resolveModel.js +107 -0
- package/lib/utils/tmpColumnUtils.js +16 -0
- package/package.json +86 -4
- package/typings/objection/index.d.ts +4104 -0
- package/typings/objection/relation-expression.d.ts +237 -0
|
@@ -0,0 +1,48 @@
|
|
|
1
|
+
'use strict';
|
|
2
|
+
|
|
3
|
+
const { clone } = require('./modelClone');
|
|
4
|
+
|
|
5
|
+
function validate(model, json, options = {}) {
|
|
6
|
+
json = json || model;
|
|
7
|
+
|
|
8
|
+
const inputJson = json;
|
|
9
|
+
const validatingModelInstance = inputJson && inputJson.$isObjectionModel;
|
|
10
|
+
|
|
11
|
+
if (options.skipValidation) {
|
|
12
|
+
return json;
|
|
13
|
+
}
|
|
14
|
+
|
|
15
|
+
if (validatingModelInstance) {
|
|
16
|
+
// Strip away relations and other internal stuff.
|
|
17
|
+
// TODO 1: This should use `json.$toJson()` since we always validate the input representation!
|
|
18
|
+
// TODO 2: This should also keep the relations in the object because some validators may
|
|
19
|
+
// depend on the relations.
|
|
20
|
+
json = clone(json, true, true);
|
|
21
|
+
// We can mutate `json` now that we took a copy of it.
|
|
22
|
+
options = Object.assign({}, options, { mutable: true });
|
|
23
|
+
}
|
|
24
|
+
|
|
25
|
+
const modelClass = model.constructor;
|
|
26
|
+
const validator = modelClass.getValidator();
|
|
27
|
+
const args = {
|
|
28
|
+
options,
|
|
29
|
+
model,
|
|
30
|
+
json,
|
|
31
|
+
ctx: Object.create(null),
|
|
32
|
+
};
|
|
33
|
+
|
|
34
|
+
validator.beforeValidate(args);
|
|
35
|
+
json = validator.validate(args);
|
|
36
|
+
validator.afterValidate(args);
|
|
37
|
+
|
|
38
|
+
if (validatingModelInstance) {
|
|
39
|
+
// If we cloned `json`, we need to copy the possible default values.
|
|
40
|
+
return inputJson.$set(json);
|
|
41
|
+
} else {
|
|
42
|
+
return json;
|
|
43
|
+
}
|
|
44
|
+
}
|
|
45
|
+
|
|
46
|
+
module.exports = {
|
|
47
|
+
validate,
|
|
48
|
+
};
|
|
@@ -0,0 +1,126 @@
|
|
|
1
|
+
'use strict';
|
|
2
|
+
|
|
3
|
+
const { isObject } = require('../utils/objectUtils');
|
|
4
|
+
|
|
5
|
+
// Property keys needs to be prefixed with a non-numeric character so that
|
|
6
|
+
// they are not considered indexes when used as object keys.
|
|
7
|
+
const PROP_KEY_PREFIX = 'k_';
|
|
8
|
+
|
|
9
|
+
function values(model, args) {
|
|
10
|
+
switch (args.length) {
|
|
11
|
+
case 1:
|
|
12
|
+
return values1(model, args);
|
|
13
|
+
case 2:
|
|
14
|
+
return values2(model, args);
|
|
15
|
+
case 3:
|
|
16
|
+
return values3(model, args);
|
|
17
|
+
default:
|
|
18
|
+
return valuesN(model, args);
|
|
19
|
+
}
|
|
20
|
+
}
|
|
21
|
+
|
|
22
|
+
function propKey(model, props) {
|
|
23
|
+
switch (props.length) {
|
|
24
|
+
case 1:
|
|
25
|
+
return propKey1(model, props);
|
|
26
|
+
case 2:
|
|
27
|
+
return propKey2(model, props);
|
|
28
|
+
case 3:
|
|
29
|
+
return propKey3(model, props);
|
|
30
|
+
default:
|
|
31
|
+
return propKeyN(model, props);
|
|
32
|
+
}
|
|
33
|
+
}
|
|
34
|
+
|
|
35
|
+
function hasProps(model, props) {
|
|
36
|
+
for (let i = 0; i < props.length; ++i) {
|
|
37
|
+
const value = model[props[i]];
|
|
38
|
+
|
|
39
|
+
if (isNullOrUndefined(value)) {
|
|
40
|
+
return false;
|
|
41
|
+
}
|
|
42
|
+
}
|
|
43
|
+
|
|
44
|
+
return true;
|
|
45
|
+
}
|
|
46
|
+
|
|
47
|
+
function values1(model, args) {
|
|
48
|
+
return [model[args[0]]];
|
|
49
|
+
}
|
|
50
|
+
|
|
51
|
+
function values2(model, args) {
|
|
52
|
+
return [model[args[0]], model[args[1]]];
|
|
53
|
+
}
|
|
54
|
+
|
|
55
|
+
function values3(model, args) {
|
|
56
|
+
return [model[args[0]], model[args[1]], model[args[2]]];
|
|
57
|
+
}
|
|
58
|
+
|
|
59
|
+
function valuesN(model, args) {
|
|
60
|
+
const ret = new Array(args.length);
|
|
61
|
+
|
|
62
|
+
for (let i = 0, l = args.length; i < l; ++i) {
|
|
63
|
+
ret[i] = model[args[i]];
|
|
64
|
+
}
|
|
65
|
+
|
|
66
|
+
return ret;
|
|
67
|
+
}
|
|
68
|
+
|
|
69
|
+
function propKey1(model, props) {
|
|
70
|
+
return PROP_KEY_PREFIX + propToStr(model[props[0]]);
|
|
71
|
+
}
|
|
72
|
+
|
|
73
|
+
function propKey2(model, props) {
|
|
74
|
+
return PROP_KEY_PREFIX + propToStr(model[props[0]]) + ',' + propToStr(model[props[1]]);
|
|
75
|
+
}
|
|
76
|
+
|
|
77
|
+
function propKey3(model, props) {
|
|
78
|
+
return (
|
|
79
|
+
PROP_KEY_PREFIX +
|
|
80
|
+
propToStr(model[props[0]]) +
|
|
81
|
+
',' +
|
|
82
|
+
propToStr(model[props[1]]) +
|
|
83
|
+
',' +
|
|
84
|
+
propToStr(model[props[2]])
|
|
85
|
+
);
|
|
86
|
+
}
|
|
87
|
+
|
|
88
|
+
function propKeyN(model, props) {
|
|
89
|
+
let key = PROP_KEY_PREFIX;
|
|
90
|
+
|
|
91
|
+
for (let i = 0, l = props.length; i < l; ++i) {
|
|
92
|
+
key += propToStr(model[props[i]]);
|
|
93
|
+
|
|
94
|
+
if (i < l - 1) {
|
|
95
|
+
key += ',';
|
|
96
|
+
}
|
|
97
|
+
}
|
|
98
|
+
|
|
99
|
+
return key;
|
|
100
|
+
}
|
|
101
|
+
|
|
102
|
+
function propToStr(value) {
|
|
103
|
+
if (value === null) {
|
|
104
|
+
return 'null';
|
|
105
|
+
} else if (value === undefined) {
|
|
106
|
+
return 'undefined';
|
|
107
|
+
} else if (Buffer.isBuffer(value)) {
|
|
108
|
+
return value.toString('hex');
|
|
109
|
+
} else if (isObject(value)) {
|
|
110
|
+
return JSON.stringify(value);
|
|
111
|
+
} else {
|
|
112
|
+
return `${value}`;
|
|
113
|
+
}
|
|
114
|
+
}
|
|
115
|
+
|
|
116
|
+
function isNullOrUndefined(val) {
|
|
117
|
+
return val === null || val === undefined;
|
|
118
|
+
}
|
|
119
|
+
|
|
120
|
+
module.exports = {
|
|
121
|
+
PROP_KEY_PREFIX,
|
|
122
|
+
propToStr,
|
|
123
|
+
values,
|
|
124
|
+
hasProps,
|
|
125
|
+
propKey,
|
|
126
|
+
};
|
|
@@ -0,0 +1,41 @@
|
|
|
1
|
+
'use strict';
|
|
2
|
+
|
|
3
|
+
function visitModels(models, modelClass, visitor) {
|
|
4
|
+
doVisit(models, modelClass, null, null, visitor);
|
|
5
|
+
}
|
|
6
|
+
|
|
7
|
+
function doVisit(models, modelClass, parent, rel, visitor) {
|
|
8
|
+
if (Array.isArray(models)) {
|
|
9
|
+
visitMany(models, modelClass, parent, rel, visitor);
|
|
10
|
+
} else if (models) {
|
|
11
|
+
visitOne(models, modelClass, parent, rel, visitor);
|
|
12
|
+
}
|
|
13
|
+
}
|
|
14
|
+
|
|
15
|
+
function visitMany(models, modelClass, parent, rel, visitor) {
|
|
16
|
+
for (let i = 0, l = models.length; i < l; ++i) {
|
|
17
|
+
visitOne(models[i], modelClass, parent, rel, visitor);
|
|
18
|
+
}
|
|
19
|
+
}
|
|
20
|
+
|
|
21
|
+
function visitOne(model, modelClass, parent, rel, visitor) {
|
|
22
|
+
if (model) {
|
|
23
|
+
visitor(model, modelClass, parent, rel);
|
|
24
|
+
}
|
|
25
|
+
|
|
26
|
+
const relationNames = modelClass.getRelationNames();
|
|
27
|
+
|
|
28
|
+
for (let i = 0, l = relationNames.length; i < l; ++i) {
|
|
29
|
+
const relationName = relationNames[i];
|
|
30
|
+
const relatedObj = model[relationName];
|
|
31
|
+
|
|
32
|
+
if (relatedObj) {
|
|
33
|
+
const relation = modelClass.getRelation(relationName);
|
|
34
|
+
doVisit(relatedObj, relation.relatedModelClass, model, relation, visitor);
|
|
35
|
+
}
|
|
36
|
+
}
|
|
37
|
+
}
|
|
38
|
+
|
|
39
|
+
module.exports = {
|
|
40
|
+
visitModels,
|
|
41
|
+
};
|
package/lib/objection.js
ADDED
|
@@ -0,0 +1,114 @@
|
|
|
1
|
+
'use strict';
|
|
2
|
+
|
|
3
|
+
const {
|
|
4
|
+
DBError,
|
|
5
|
+
UniqueViolationError,
|
|
6
|
+
NotNullViolationError,
|
|
7
|
+
ForeignKeyViolationError,
|
|
8
|
+
ConstraintViolationError,
|
|
9
|
+
CheckViolationError,
|
|
10
|
+
DataError,
|
|
11
|
+
} = require('db-errors');
|
|
12
|
+
const { Model: NativeModel } = require('./model/Model');
|
|
13
|
+
const { QueryBuilder: NativeQueryBuilder } = require('./queryBuilder/QueryBuilder');
|
|
14
|
+
const { QueryBuilderBase } = require('./queryBuilder/QueryBuilderBase');
|
|
15
|
+
const { QueryBuilderOperation } = require('./queryBuilder/operations/QueryBuilderOperation');
|
|
16
|
+
const { RelationExpression } = require('./queryBuilder/RelationExpression');
|
|
17
|
+
const { ValidationError } = require('./model/ValidationError');
|
|
18
|
+
const { NotFoundError } = require('./model/NotFoundError');
|
|
19
|
+
const { AjvValidator: NativeAjvValidator } = require('./model/AjvValidator');
|
|
20
|
+
const { Validator: NativeValidator } = require('./model/Validator');
|
|
21
|
+
const { Relation } = require('./relations/Relation');
|
|
22
|
+
const { HasOneRelation } = require('./relations/hasOne/HasOneRelation');
|
|
23
|
+
const { HasManyRelation } = require('./relations/hasMany/HasManyRelation');
|
|
24
|
+
const { BelongsToOneRelation } = require('./relations/belongsToOne/BelongsToOneRelation');
|
|
25
|
+
const { HasOneThroughRelation } = require('./relations/hasOneThrough/HasOneThroughRelation');
|
|
26
|
+
const { ManyToManyRelation } = require('./relations/manyToMany/ManyToManyRelation');
|
|
27
|
+
const { transaction } = require('./transaction');
|
|
28
|
+
const { initialize } = require('./initialize');
|
|
29
|
+
|
|
30
|
+
const {
|
|
31
|
+
snakeCaseMappers,
|
|
32
|
+
knexSnakeCaseMappers,
|
|
33
|
+
knexIdentifierMapping,
|
|
34
|
+
} = require('./utils/identifierMapping');
|
|
35
|
+
const { compose, mixin } = require('./utils/mixin');
|
|
36
|
+
const { ref } = require('./queryBuilder/ReferenceBuilder');
|
|
37
|
+
const { val } = require('./queryBuilder/ValueBuilder');
|
|
38
|
+
const { raw } = require('./queryBuilder/RawBuilder');
|
|
39
|
+
const { fn } = require('./queryBuilder/FunctionBuilder');
|
|
40
|
+
const { inherit } = require('../lib/utils/classUtils');
|
|
41
|
+
|
|
42
|
+
// We need to wrap the classes, that people can inherit, with ES5 classes
|
|
43
|
+
// so that babel is able to use ES5 inheritance. sigh... Maybe people
|
|
44
|
+
// should stop transpiling node apps to ES5 in the year 2019? Node 6
|
|
45
|
+
// with full class support was released three years ago.
|
|
46
|
+
|
|
47
|
+
function Model() {
|
|
48
|
+
// Nothing to do here.
|
|
49
|
+
}
|
|
50
|
+
|
|
51
|
+
function QueryBuilder(...args) {
|
|
52
|
+
NativeQueryBuilder.init(this, ...args);
|
|
53
|
+
}
|
|
54
|
+
|
|
55
|
+
function Validator(...args) {
|
|
56
|
+
NativeValidator.init(this, ...args);
|
|
57
|
+
}
|
|
58
|
+
|
|
59
|
+
function AjvValidator(...args) {
|
|
60
|
+
NativeAjvValidator.init(this, ...args);
|
|
61
|
+
}
|
|
62
|
+
|
|
63
|
+
// Returns the relation mappings as is. This only exists for typescript: it
|
|
64
|
+
// preserves the exact types of the mappings (like the names of `extra`
|
|
65
|
+
// columns) so that the relation types can be inferred from them.
|
|
66
|
+
function defineRelations(relationMappings) {
|
|
67
|
+
return relationMappings;
|
|
68
|
+
}
|
|
69
|
+
|
|
70
|
+
inherit(Model, NativeModel);
|
|
71
|
+
inherit(QueryBuilder, NativeQueryBuilder);
|
|
72
|
+
inherit(Validator, NativeValidator);
|
|
73
|
+
inherit(AjvValidator, NativeAjvValidator);
|
|
74
|
+
|
|
75
|
+
Model.QueryBuilder = QueryBuilder;
|
|
76
|
+
|
|
77
|
+
module.exports = {
|
|
78
|
+
Model,
|
|
79
|
+
QueryBuilder,
|
|
80
|
+
QueryBuilderBase,
|
|
81
|
+
QueryBuilderOperation,
|
|
82
|
+
RelationExpression,
|
|
83
|
+
ValidationError,
|
|
84
|
+
NotFoundError,
|
|
85
|
+
AjvValidator,
|
|
86
|
+
Validator,
|
|
87
|
+
Relation,
|
|
88
|
+
HasOneRelation,
|
|
89
|
+
HasManyRelation,
|
|
90
|
+
BelongsToOneRelation,
|
|
91
|
+
HasOneThroughRelation,
|
|
92
|
+
ManyToManyRelation,
|
|
93
|
+
|
|
94
|
+
transaction,
|
|
95
|
+
initialize,
|
|
96
|
+
compose,
|
|
97
|
+
mixin,
|
|
98
|
+
defineRelations,
|
|
99
|
+
ref,
|
|
100
|
+
val,
|
|
101
|
+
raw,
|
|
102
|
+
fn,
|
|
103
|
+
snakeCaseMappers,
|
|
104
|
+
knexSnakeCaseMappers,
|
|
105
|
+
knexIdentifierMapping,
|
|
106
|
+
|
|
107
|
+
DBError,
|
|
108
|
+
UniqueViolationError,
|
|
109
|
+
NotNullViolationError,
|
|
110
|
+
ForeignKeyViolationError,
|
|
111
|
+
ConstraintViolationError,
|
|
112
|
+
CheckViolationError,
|
|
113
|
+
DataError,
|
|
114
|
+
};
|
|
@@ -0,0 +1,339 @@
|
|
|
1
|
+
'use strict';
|
|
2
|
+
|
|
3
|
+
const { RawBuilder, normalizeRawArgs } = require('./RawBuilder');
|
|
4
|
+
const { ref } = require('./ReferenceBuilder');
|
|
5
|
+
const { expression } = require('./expressions');
|
|
6
|
+
const { asArray, asSingle, isNumber, isObject, isPlainObject } = require('../utils/objectUtils');
|
|
7
|
+
const { getDialect, isMySql } = require('../utils/knexUtils');
|
|
8
|
+
const { FUNCTIONS, buildOrderBy } = require('./functions/mysql');
|
|
9
|
+
|
|
10
|
+
class FunctionBuilder extends RawBuilder {
|
|
11
|
+
constructor(sql, args) {
|
|
12
|
+
super(sql, args);
|
|
13
|
+
// The name of the `fn` helper of a MySQL only function.
|
|
14
|
+
this._mysqlFunction = null;
|
|
15
|
+
// For aggregate functions, the number of the first `?`s that are aggregated,
|
|
16
|
+
// or '*' for `COUNT(*)`.
|
|
17
|
+
this._aggregated = null;
|
|
18
|
+
// The name of the `fn` helper of a window function.
|
|
19
|
+
this._windowFunction = null;
|
|
20
|
+
this._filters = [];
|
|
21
|
+
this._over = null;
|
|
22
|
+
}
|
|
23
|
+
|
|
24
|
+
// Only aggregates the rows that match `condition`:
|
|
25
|
+
//
|
|
26
|
+
// fn.count().filter(Pet.ref('species').eq('dog'))
|
|
27
|
+
//
|
|
28
|
+
// is `COUNT(CASE WHEN species = 'dog' THEN 1 END)`. That works on every
|
|
29
|
+
// database, unlike `FILTER (WHERE ...)`.
|
|
30
|
+
filter(condition) {
|
|
31
|
+
if (this._aggregated === null) {
|
|
32
|
+
throw new Error(
|
|
33
|
+
'filter() only works with the aggregate functions of fn, like fn.count() and fn.sum().',
|
|
34
|
+
);
|
|
35
|
+
}
|
|
36
|
+
|
|
37
|
+
this._filters.push(condition);
|
|
38
|
+
return this;
|
|
39
|
+
}
|
|
40
|
+
|
|
41
|
+
// `fn.rowNumber().over({ partitionBy: 'ownerId', orderBy: ['createdAt', 'desc'] })`.
|
|
42
|
+
over(window = {}) {
|
|
43
|
+
for (const key of Object.keys(window)) {
|
|
44
|
+
if (key !== 'partitionBy' && key !== 'orderBy') {
|
|
45
|
+
throw new Error(`over(): unknown option "${key}". Use partitionBy and orderBy.`);
|
|
46
|
+
}
|
|
47
|
+
}
|
|
48
|
+
|
|
49
|
+
this._over = window;
|
|
50
|
+
return this;
|
|
51
|
+
}
|
|
52
|
+
|
|
53
|
+
toKnexRaw(builder) {
|
|
54
|
+
const knex = builder.unsafeKnex();
|
|
55
|
+
|
|
56
|
+
if (this._mysqlFunction && knex && !isMySql(knex)) {
|
|
57
|
+
throw new Error(
|
|
58
|
+
`fn.${this._mysqlFunction}() is a MySQL function and can't be used with ${getDialect(knex)}.`,
|
|
59
|
+
);
|
|
60
|
+
}
|
|
61
|
+
|
|
62
|
+
if (this._windowFunction && !this._over) {
|
|
63
|
+
throw new Error(`fn.${this._windowFunction}() is a window function and needs over().`);
|
|
64
|
+
}
|
|
65
|
+
|
|
66
|
+
if (this._filters.length === 0 && !this._over) {
|
|
67
|
+
return super.toKnexRaw(builder);
|
|
68
|
+
}
|
|
69
|
+
|
|
70
|
+
return new RawBuilder(...this._buildFilterAndOver()).as(this._as).toKnexRaw(builder);
|
|
71
|
+
}
|
|
72
|
+
|
|
73
|
+
_buildFilterAndOver() {
|
|
74
|
+
let sql = this._sql;
|
|
75
|
+
let args = this._args;
|
|
76
|
+
|
|
77
|
+
if (this._filters.length !== 0) {
|
|
78
|
+
const condition =
|
|
79
|
+
this._filters.length === 1
|
|
80
|
+
? this._filters[0]
|
|
81
|
+
: expression(`(${this._filters.map(() => '?').join(' AND ')})`, this._filters);
|
|
82
|
+
|
|
83
|
+
if (this._aggregated === '*') {
|
|
84
|
+
sql = sql.replace('*', 'CASE WHEN ? THEN 1 END');
|
|
85
|
+
args = [condition];
|
|
86
|
+
} else {
|
|
87
|
+
const filtered = [];
|
|
88
|
+
let i = 0;
|
|
89
|
+
|
|
90
|
+
sql = sql.replace(/\?/g, () => {
|
|
91
|
+
const arg = args[i++];
|
|
92
|
+
|
|
93
|
+
if (i <= this._aggregated) {
|
|
94
|
+
filtered.push(condition, arg);
|
|
95
|
+
return 'CASE WHEN ? THEN ? END';
|
|
96
|
+
} else {
|
|
97
|
+
filtered.push(arg);
|
|
98
|
+
return '?';
|
|
99
|
+
}
|
|
100
|
+
});
|
|
101
|
+
|
|
102
|
+
args = filtered;
|
|
103
|
+
}
|
|
104
|
+
}
|
|
105
|
+
|
|
106
|
+
if (this._over) {
|
|
107
|
+
const [overSql, overArgs] = buildOver(this._over);
|
|
108
|
+
sql = `${sql} OVER (${overSql})`;
|
|
109
|
+
args = [...args, ...overArgs];
|
|
110
|
+
}
|
|
111
|
+
|
|
112
|
+
return [sql, args];
|
|
113
|
+
}
|
|
114
|
+
}
|
|
115
|
+
|
|
116
|
+
// `fn.case()` is `CASE WHEN condition THEN result ... END` and `fn.case(value)` is
|
|
117
|
+
// `CASE value WHEN other THEN result ... END`.
|
|
118
|
+
class CaseBuilder extends FunctionBuilder {
|
|
119
|
+
constructor(hasValue, value) {
|
|
120
|
+
super('', []);
|
|
121
|
+
this._hasValue = hasValue;
|
|
122
|
+
this._value = value;
|
|
123
|
+
this._whens = [];
|
|
124
|
+
this._hasElse = false;
|
|
125
|
+
this._else = null;
|
|
126
|
+
}
|
|
127
|
+
|
|
128
|
+
when(condition, result) {
|
|
129
|
+
this._whens.push([condition, result]);
|
|
130
|
+
return this;
|
|
131
|
+
}
|
|
132
|
+
|
|
133
|
+
else(result) {
|
|
134
|
+
this._hasElse = true;
|
|
135
|
+
this._else = result;
|
|
136
|
+
return this;
|
|
137
|
+
}
|
|
138
|
+
|
|
139
|
+
toKnexRaw(builder) {
|
|
140
|
+
if (this._whens.length === 0) {
|
|
141
|
+
throw new Error('fn.case() needs at least one when().');
|
|
142
|
+
}
|
|
143
|
+
|
|
144
|
+
const args = this._hasValue ? [this._value] : [];
|
|
145
|
+
let sql = this._hasValue ? '(CASE ?' : '(CASE';
|
|
146
|
+
|
|
147
|
+
for (const [condition, result] of this._whens) {
|
|
148
|
+
sql += ' WHEN ? THEN ?';
|
|
149
|
+
args.push(condition, result);
|
|
150
|
+
}
|
|
151
|
+
|
|
152
|
+
if (this._hasElse) {
|
|
153
|
+
sql += ' ELSE ?';
|
|
154
|
+
args.push(this._else);
|
|
155
|
+
}
|
|
156
|
+
|
|
157
|
+
this._sql = `${sql} END)`;
|
|
158
|
+
this._args = args;
|
|
159
|
+
|
|
160
|
+
return super.toKnexRaw(builder);
|
|
161
|
+
}
|
|
162
|
+
}
|
|
163
|
+
|
|
164
|
+
// The value an upsert tried to insert: `VALUES(col)` on MySQL, `excluded.col` on
|
|
165
|
+
// postgres and SQLite.
|
|
166
|
+
class ExcludedBuilder extends FunctionBuilder {
|
|
167
|
+
constructor(column) {
|
|
168
|
+
super('', []);
|
|
169
|
+
|
|
170
|
+
if (isObject(column) && column.isObjectionReferenceBuilder) {
|
|
171
|
+
column = column.column;
|
|
172
|
+
}
|
|
173
|
+
|
|
174
|
+
if (typeof column !== 'string') {
|
|
175
|
+
throw new Error('fn.excluded() takes a column name.');
|
|
176
|
+
}
|
|
177
|
+
|
|
178
|
+
this._column = column;
|
|
179
|
+
}
|
|
180
|
+
|
|
181
|
+
toKnexRaw(builder) {
|
|
182
|
+
if (isMySql(builder.unsafeKnex())) {
|
|
183
|
+
this._sql = 'VALUES(??)';
|
|
184
|
+
this._args = [this._column];
|
|
185
|
+
} else {
|
|
186
|
+
this._sql = '??';
|
|
187
|
+
this._args = [`excluded.${this._column}`];
|
|
188
|
+
}
|
|
189
|
+
|
|
190
|
+
return super.toKnexRaw(builder);
|
|
191
|
+
}
|
|
192
|
+
}
|
|
193
|
+
|
|
194
|
+
function buildOver({ partitionBy, orderBy }) {
|
|
195
|
+
const parts = [];
|
|
196
|
+
const args = [];
|
|
197
|
+
|
|
198
|
+
if (partitionBy !== undefined) {
|
|
199
|
+
const columns = asArray(partitionBy).map((it) => (typeof it === 'string' ? ref(it) : it));
|
|
200
|
+
parts.push(`PARTITION BY ${columns.map(() => '?').join(', ')}`);
|
|
201
|
+
args.push(...columns);
|
|
202
|
+
}
|
|
203
|
+
|
|
204
|
+
if (orderBy !== undefined) {
|
|
205
|
+
const [orderBySql, orderByArgs] = buildOrderBy('over()', orderBy);
|
|
206
|
+
parts.push(`ORDER BY ${orderBySql}`);
|
|
207
|
+
args.push(...orderByArgs);
|
|
208
|
+
}
|
|
209
|
+
|
|
210
|
+
return [parts.join(' '), args];
|
|
211
|
+
}
|
|
212
|
+
|
|
213
|
+
function fn(...argsIn) {
|
|
214
|
+
const { sql, args } = normalizeRawArgs(argsIn);
|
|
215
|
+
return new FunctionBuilder(`${sql}(${args.map(() => '?').join(', ')})`, args);
|
|
216
|
+
}
|
|
217
|
+
|
|
218
|
+
for (const func of ['coalesce', 'concat', 'upper', 'lower']) {
|
|
219
|
+
fn[func] = (...args) => fn(func.toUpperCase(), args);
|
|
220
|
+
}
|
|
221
|
+
|
|
222
|
+
for (const func of ['sum', 'avg', 'min', 'max']) {
|
|
223
|
+
fn[func] = (...args) => aggregate(fn(func.toUpperCase(), args), args.length);
|
|
224
|
+
}
|
|
225
|
+
|
|
226
|
+
// `fn.count()` and `fn.count('*')` are `COUNT(*)`.
|
|
227
|
+
fn.count = (...args) => {
|
|
228
|
+
if (args.length === 0 || (args.length === 1 && args[0] === '*')) {
|
|
229
|
+
return aggregate(new FunctionBuilder('COUNT(*)', []), '*');
|
|
230
|
+
}
|
|
231
|
+
|
|
232
|
+
return aggregate(fn('COUNT', args), args.length);
|
|
233
|
+
};
|
|
234
|
+
|
|
235
|
+
fn.now = (precision) => {
|
|
236
|
+
precision = parseInt(asSingle(precision), 10);
|
|
237
|
+
|
|
238
|
+
if (isNaN(precision) || !isNumber(precision)) {
|
|
239
|
+
precision = 6;
|
|
240
|
+
}
|
|
241
|
+
|
|
242
|
+
// We need to use a literal precision instead of a binding here
|
|
243
|
+
// for the CURRENT_TIMESTAMP to work. This is okay here since we
|
|
244
|
+
// make sure `precision` is a number. There's no chance of SQL
|
|
245
|
+
// injection here.
|
|
246
|
+
return new FunctionBuilder(`CURRENT_TIMESTAMP(${precision})`, []);
|
|
247
|
+
};
|
|
248
|
+
|
|
249
|
+
fn.case = (...args) => new CaseBuilder(args.length !== 0, args[0]);
|
|
250
|
+
|
|
251
|
+
fn.exists = (subquery) => {
|
|
252
|
+
if (!isObject(subquery) || !subquery.isObjectionQueryBuilderBase) {
|
|
253
|
+
throw new Error('fn.exists() takes a query.');
|
|
254
|
+
}
|
|
255
|
+
|
|
256
|
+
return new FunctionBuilder('(EXISTS ?)', [subquery]);
|
|
257
|
+
};
|
|
258
|
+
|
|
259
|
+
fn.excluded = (column) => new ExcludedBuilder(column);
|
|
260
|
+
|
|
261
|
+
for (const [name, sql, , , options = {}] of FUNCTIONS) {
|
|
262
|
+
// `defineProperty` since `fn.length` is a read-only property of functions.
|
|
263
|
+
Object.defineProperty(fn, name, {
|
|
264
|
+
configurable: true,
|
|
265
|
+
enumerable: true,
|
|
266
|
+
writable: true,
|
|
267
|
+
value: createMySqlFunction(name, sql, options),
|
|
268
|
+
});
|
|
269
|
+
}
|
|
270
|
+
|
|
271
|
+
function createMySqlFunction(name, sql, options) {
|
|
272
|
+
return (...args) => {
|
|
273
|
+
const [text, values] = options.build
|
|
274
|
+
? options.build(name, sql, args)
|
|
275
|
+
: [`${sql}(${args.map(() => '?').join(', ')})`, args];
|
|
276
|
+
|
|
277
|
+
const builder = createWithJsonArgs(text, values, options.json);
|
|
278
|
+
|
|
279
|
+
if (!options.portable) {
|
|
280
|
+
builder._mysqlFunction = name;
|
|
281
|
+
}
|
|
282
|
+
|
|
283
|
+
if (options.window) {
|
|
284
|
+
builder._windowFunction = name;
|
|
285
|
+
}
|
|
286
|
+
|
|
287
|
+
if (options.aggregate) {
|
|
288
|
+
aggregate(builder, options.aggregate === true ? args.length : options.aggregate(args));
|
|
289
|
+
}
|
|
290
|
+
|
|
291
|
+
return builder;
|
|
292
|
+
};
|
|
293
|
+
}
|
|
294
|
+
|
|
295
|
+
function aggregate(builder, aggregated) {
|
|
296
|
+
builder._aggregated = aggregated;
|
|
297
|
+
return builder;
|
|
298
|
+
}
|
|
299
|
+
|
|
300
|
+
// Objects and arrays passed as JSON documents are cast to JSON. Without the cast
|
|
301
|
+
// MySQL would get a string (or knex would expand an array into a list).
|
|
302
|
+
function createWithJsonArgs(text, values, json) {
|
|
303
|
+
const casts = values.map((value, i) => isJsonArg(json, i) && isJsonValue(value));
|
|
304
|
+
let i = 0;
|
|
305
|
+
|
|
306
|
+
text = text.replace(/\?/g, () => (casts[i++] ? 'CAST(? AS JSON)' : '?'));
|
|
307
|
+
values = values.map((value, i) => (casts[i] ? JSON.stringify(value) : value));
|
|
308
|
+
|
|
309
|
+
return new FunctionBuilder(text, values);
|
|
310
|
+
}
|
|
311
|
+
|
|
312
|
+
function isJsonArg(json, i) {
|
|
313
|
+
if (json === 'all') {
|
|
314
|
+
return true;
|
|
315
|
+
} else if (json === 'even') {
|
|
316
|
+
return i % 2 === 0;
|
|
317
|
+
} else if (json === 'odd') {
|
|
318
|
+
return i % 2 === 1;
|
|
319
|
+
} else {
|
|
320
|
+
return Array.isArray(json) && json.includes(i);
|
|
321
|
+
}
|
|
322
|
+
}
|
|
323
|
+
|
|
324
|
+
function isJsonValue(value) {
|
|
325
|
+
return Array.isArray(value) || isPlainObject(value);
|
|
326
|
+
}
|
|
327
|
+
|
|
328
|
+
Object.defineProperties(FunctionBuilder.prototype, {
|
|
329
|
+
isObjectionFunctionBuilder: {
|
|
330
|
+
enumerable: false,
|
|
331
|
+
writable: false,
|
|
332
|
+
value: true,
|
|
333
|
+
},
|
|
334
|
+
});
|
|
335
|
+
|
|
336
|
+
module.exports = {
|
|
337
|
+
FunctionBuilder,
|
|
338
|
+
fn,
|
|
339
|
+
};
|
|
@@ -0,0 +1,29 @@
|
|
|
1
|
+
'use strict';
|
|
2
|
+
|
|
3
|
+
class InternalOptions {
|
|
4
|
+
constructor() {
|
|
5
|
+
this.skipUndefined = false;
|
|
6
|
+
this.keepImplicitJoinProps = false;
|
|
7
|
+
this.returnImmediatelyValue = undefined;
|
|
8
|
+
this.isInternalQuery = false;
|
|
9
|
+
this.debug = false;
|
|
10
|
+
this.schema = undefined;
|
|
11
|
+
}
|
|
12
|
+
|
|
13
|
+
clone() {
|
|
14
|
+
const copy = new this.constructor();
|
|
15
|
+
|
|
16
|
+
copy.skipUndefined = this.skipUndefined;
|
|
17
|
+
copy.keepImplicitJoinProps = this.keepImplicitJoinProps;
|
|
18
|
+
copy.returnImmediatelyValue = this.returnImmediatelyValue;
|
|
19
|
+
copy.isInternalQuery = this.isInternalQuery;
|
|
20
|
+
copy.debug = this.debug;
|
|
21
|
+
copy.schema = this.schema;
|
|
22
|
+
|
|
23
|
+
return copy;
|
|
24
|
+
}
|
|
25
|
+
}
|
|
26
|
+
|
|
27
|
+
module.exports = {
|
|
28
|
+
InternalOptions,
|
|
29
|
+
};
|