@abedmurrar/objection 4.0.0-alpha.0 → 4.0.0-beta.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 +103 -67
- package/lib/index.mjs +6 -0
- package/lib/initialize.js +6 -9
- package/lib/model/AjvValidator.js +235 -282
- package/lib/model/Model.js +653 -823
- package/lib/model/ModifierNotFoundError.js +5 -7
- package/lib/model/NotFoundError.js +14 -18
- package/lib/model/RelationDoesNotExistError.js +6 -9
- package/lib/model/ValidationError.js +33 -41
- package/lib/model/Validator.js +15 -21
- package/lib/model/getModel.js +1 -3
- package/lib/model/graph/ModelGraph.js +44 -64
- package/lib/model/graph/ModelGraphBuilder.js +164 -217
- package/lib/model/graph/ModelGraphEdge.js +31 -43
- package/lib/model/graph/ModelGraphNode.js +192 -231
- package/lib/model/inheritModel.js +9 -18
- package/lib/model/modelBindKnex.js +48 -62
- package/lib/model/modelClone.js +79 -98
- package/lib/model/modelColPropMap.js +16 -26
- package/lib/model/modelConcurrency.js +24 -0
- package/lib/model/modelId.js +44 -49
- package/lib/model/modelJsonAttributes.js +56 -76
- package/lib/model/modelParseRelations.js +68 -67
- package/lib/model/modelQueryProps.js +76 -107
- package/lib/model/modelSet.js +92 -112
- package/lib/model/modelTableMetadata.js +60 -72
- package/lib/model/modelToJson.js +120 -150
- package/lib/model/modelUtils.js +30 -37
- package/lib/model/modelValidate.js +34 -42
- package/lib/model/modelValues.js +78 -91
- package/lib/model/modelVisitor.js +22 -29
- package/lib/objection.js +42 -67
- package/lib/queryBuilder/FunctionBuilder.js +231 -279
- package/lib/queryBuilder/InternalOptions.js +19 -24
- package/lib/queryBuilder/JoinBuilder.js +116 -149
- package/lib/queryBuilder/QueryBuilder.js +1009 -1271
- package/lib/queryBuilder/QueryBuilderBase.js +647 -865
- package/lib/queryBuilder/QueryBuilderContext.js +14 -21
- package/lib/queryBuilder/QueryBuilderContextBase.js +20 -29
- package/lib/queryBuilder/QueryBuilderOperationSupport.js +462 -536
- package/lib/queryBuilder/QueryBuilderUserContext.js +19 -25
- package/lib/queryBuilder/RawBuilder.js +58 -73
- package/lib/queryBuilder/ReferenceBuilder.js +232 -270
- package/lib/queryBuilder/RelationExpression.js +322 -380
- package/lib/queryBuilder/StaticHookArguments.js +94 -106
- package/lib/queryBuilder/ValueBuilder.js +100 -112
- package/lib/queryBuilder/expressions.js +86 -116
- package/lib/queryBuilder/functions/mysql.js +642 -697
- package/lib/queryBuilder/graph/GraphAction.js +44 -59
- package/lib/queryBuilder/graph/GraphData.js +7 -9
- package/lib/queryBuilder/graph/GraphFetcher.js +115 -151
- package/lib/queryBuilder/graph/GraphNodeDbExistence.js +114 -152
- package/lib/queryBuilder/graph/GraphOperation.js +16 -22
- package/lib/queryBuilder/graph/GraphOptions.js +143 -185
- package/lib/queryBuilder/graph/GraphUpsert.js +167 -241
- package/lib/queryBuilder/graph/delete/GraphDelete.js +8 -13
- package/lib/queryBuilder/graph/delete/GraphDeleteAction.js +44 -62
- package/lib/queryBuilder/graph/insert/GraphInsert.js +99 -128
- package/lib/queryBuilder/graph/insert/GraphInsertAction.js +151 -193
- package/lib/queryBuilder/graph/insert/JoinRowGraphInsertAction.js +57 -79
- package/lib/queryBuilder/graph/patch/GraphPatch.js +8 -13
- package/lib/queryBuilder/graph/patch/GraphPatchAction.js +181 -234
- package/lib/queryBuilder/graph/recursiveUpsert/GraphRecursiveUpsert.js +16 -21
- package/lib/queryBuilder/graph/recursiveUpsert/GraphRecursiveUpsertAction.js +80 -110
- package/lib/queryBuilder/join/JoinResultColumn.js +14 -18
- package/lib/queryBuilder/join/JoinResultParser.js +88 -101
- package/lib/queryBuilder/join/RelationJoiner.js +205 -288
- package/lib/queryBuilder/join/TableNode.js +117 -144
- package/lib/queryBuilder/join/TableTree.js +60 -76
- package/lib/queryBuilder/join/utils.js +10 -16
- package/lib/queryBuilder/operations/DelegateOperation.js +93 -133
- package/lib/queryBuilder/operations/DeleteOperation.js +33 -37
- package/lib/queryBuilder/operations/FindByIdOperation.js +20 -28
- package/lib/queryBuilder/operations/FindByIdsOperation.js +17 -23
- package/lib/queryBuilder/operations/FindOperation.js +114 -131
- package/lib/queryBuilder/operations/FirstOperation.js +14 -19
- package/lib/queryBuilder/operations/FromOperation.js +69 -88
- package/lib/queryBuilder/operations/InsertAndFetchOperation.js +28 -39
- package/lib/queryBuilder/operations/InsertGraphAndFetchOperation.js +25 -35
- package/lib/queryBuilder/operations/InsertGraphOperation.js +77 -98
- package/lib/queryBuilder/operations/InsertOperation.js +132 -165
- package/lib/queryBuilder/operations/InstanceDeleteOperation.js +27 -36
- package/lib/queryBuilder/operations/InstanceFindOperation.js +9 -13
- package/lib/queryBuilder/operations/InstanceInsertOperation.js +13 -17
- package/lib/queryBuilder/operations/InstanceUpdateOperation.js +32 -46
- package/lib/queryBuilder/operations/JoinRelatedOperation.js +66 -86
- package/lib/queryBuilder/operations/KnexOperation.js +4 -7
- package/lib/queryBuilder/operations/MergeOperation.js +32 -45
- package/lib/queryBuilder/operations/ObjectionToKnexConvertingOperation.js +131 -150
- package/lib/queryBuilder/operations/OnBuildKnexOperation.js +17 -23
- package/lib/queryBuilder/operations/OnBuildOperation.js +17 -23
- package/lib/queryBuilder/operations/OnErrorOperation.js +17 -23
- package/lib/queryBuilder/operations/QueryBuilderOperation.js +231 -270
- package/lib/queryBuilder/operations/RangeOperation.js +30 -40
- package/lib/queryBuilder/operations/RelateOperation.js +14 -21
- package/lib/queryBuilder/operations/ReturningOperation.js +12 -18
- package/lib/queryBuilder/operations/RunAfterOperation.js +17 -23
- package/lib/queryBuilder/operations/RunBeforeOperation.js +17 -23
- package/lib/queryBuilder/operations/UnrelateOperation.js +12 -17
- package/lib/queryBuilder/operations/UpdateAndFetchOperation.js +48 -65
- package/lib/queryBuilder/operations/UpdateOperation.js +104 -129
- package/lib/queryBuilder/operations/UpsertGraphAndFetchOperation.js +25 -35
- package/lib/queryBuilder/operations/UpsertGraphOperation.js +36 -55
- package/lib/queryBuilder/operations/WhereCompositeOperation.js +30 -39
- package/lib/queryBuilder/operations/WhereInOperation.js +34 -36
- package/lib/queryBuilder/operations/eager/EagerOperation.js +37 -52
- package/lib/queryBuilder/operations/eager/JoinEagerOperation.js +34 -44
- package/lib/queryBuilder/operations/eager/NaiveEagerOperation.js +4 -7
- package/lib/queryBuilder/operations/eager/WhereInEagerOperation.js +193 -251
- package/lib/queryBuilder/operations/jsonApi/WhereJsonHasOperation.js +18 -25
- package/lib/queryBuilder/operations/jsonApi/WhereJsonNotObjectOperation.js +25 -41
- package/lib/queryBuilder/operations/jsonApi/WhereJsonOperation.js +19 -20
- package/lib/queryBuilder/operations/jsonApi/WhereJsonOverlapsOperation.js +23 -21
- package/lib/queryBuilder/operations/jsonApi/mysqlJsonApi.js +106 -118
- package/lib/queryBuilder/operations/jsonApi/postgresJsonApi.js +79 -108
- package/lib/queryBuilder/operations/select/SelectOperation.js +37 -53
- package/lib/queryBuilder/operations/select/Selection.js +94 -105
- package/lib/queryBuilder/operations/whereInComposite/WhereInCompositeMsSqlOperation.js +68 -101
- package/lib/queryBuilder/operations/whereInComposite/WhereInCompositeOperation.js +65 -75
- package/lib/queryBuilder/operations/whereInComposite/WhereInCompositeSqliteOperation.js +75 -84
- package/lib/queryBuilder/parsers/jsonFieldExpressionParser.js +760 -859
- package/lib/queryBuilder/parsers/relationExpressionParser.js +840 -952
- package/lib/queryBuilder/transformations/CompositeQueryTransformation.js +10 -15
- package/lib/queryBuilder/transformations/QueryTransformation.js +4 -6
- package/lib/queryBuilder/transformations/WrapMysqlModifySubqueryTransformation.js +35 -49
- package/lib/queryBuilder/transformations/index.js +3 -9
- package/lib/queryBuilder/whereScopes.js +158 -186
- package/lib/relations/Relation.js +279 -363
- package/lib/relations/RelationDeleteOperation.js +16 -22
- package/lib/relations/RelationFindOperation.js +100 -133
- package/lib/relations/RelationInsertOperation.js +19 -27
- package/lib/relations/RelationOwner.js +167 -204
- package/lib/relations/RelationProperty.js +177 -217
- package/lib/relations/RelationUpdateOperation.js +16 -23
- package/lib/relations/belongsToOne/BelongsToOneDeleteOperation.js +11 -16
- package/lib/relations/belongsToOne/BelongsToOneInsertOperation.js +24 -35
- package/lib/relations/belongsToOne/BelongsToOneRelateOperation.js +30 -41
- package/lib/relations/belongsToOne/BelongsToOneRelation.js +33 -41
- package/lib/relations/belongsToOne/BelongsToOneUnrelateOperation.js +38 -53
- package/lib/relations/hasMany/HasManyInsertOperation.js +35 -51
- package/lib/relations/hasMany/HasManyRelateOperation.js +38 -54
- package/lib/relations/hasMany/HasManyRelation.js +30 -38
- package/lib/relations/hasMany/HasManyUnrelateOperation.js +19 -26
- package/lib/relations/hasOne/HasOneRelation.js +4 -7
- package/lib/relations/hasOneThrough/HasOneThroughRelation.js +4 -7
- package/lib/relations/manyToMany/ManyToManyModifyMixin.js +67 -90
- package/lib/relations/manyToMany/ManyToManyRelation.js +349 -451
- package/lib/relations/manyToMany/ManyToManySqliteModifyMixin.js +19 -26
- package/lib/relations/manyToMany/delete/ManyToManyDeleteOperation.js +3 -5
- package/lib/relations/manyToMany/delete/ManyToManyDeleteOperationBase.js +14 -20
- package/lib/relations/manyToMany/delete/ManyToManyDeleteSqliteOperation.js +3 -7
- package/lib/relations/manyToMany/find/ManyToManyFindOperation.js +71 -94
- package/lib/relations/manyToMany/insert/ManyToManyInsertOperation.js +31 -43
- package/lib/relations/manyToMany/relate/ManyToManyRelateOperation.js +50 -67
- package/lib/relations/manyToMany/unrelate/ManyToManyUnrelateOperation.js +4 -7
- package/lib/relations/manyToMany/unrelate/ManyToManyUnrelateOperationBase.js +20 -27
- package/lib/relations/manyToMany/unrelate/ManyToManyUnrelateSqliteOperation.js +5 -10
- package/lib/relations/manyToMany/update/ManyToManyUpdateOperation.js +3 -5
- package/lib/relations/manyToMany/update/ManyToManyUpdateOperationBase.js +55 -72
- package/lib/relations/manyToMany/update/ManyToManyUpdateSqliteOperation.js +3 -7
- package/lib/transaction.js +53 -83
- package/lib/utils/assert.js +15 -21
- package/lib/utils/buildUtils.js +17 -19
- package/lib/utils/classUtils.js +7 -11
- package/lib/utils/cloneDeep.js +365 -0
- package/lib/utils/createModifier.js +36 -41
- package/lib/utils/deprecate.js +8 -12
- package/lib/utils/identifierMapping.js +153 -192
- package/lib/utils/internalPropUtils.js +4 -8
- package/lib/utils/knexUtils.js +61 -56
- package/lib/utils/mixin.js +16 -23
- package/lib/utils/mysqlJsonPath.js +55 -40
- package/lib/utils/normalizeIds.js +110 -127
- package/lib/utils/objectUtils.js +236 -338
- package/lib/utils/parseFieldExpression.js +66 -44
- package/lib/utils/postgresJsonPath.js +48 -62
- package/lib/utils/promiseUtils/after.js +10 -13
- package/lib/utils/promiseUtils/afterReturn.js +10 -13
- package/lib/utils/promiseUtils/index.js +15 -17
- package/lib/utils/promiseUtils/isPromise.js +6 -9
- package/lib/utils/promiseUtils/map.js +75 -89
- package/lib/utils/promiseUtils/mapAfterAllReturn.js +17 -23
- package/lib/utils/promiseUtils/try.js +15 -18
- package/lib/utils/resolveModel.js +91 -94
- package/lib/utils/tmpColumnUtils.js +4 -13
- package/package.json +50 -23
- package/typings/objection/index.d.mts +362 -0
- package/typings/objection/index.d.ts +36 -9
- package/lib/.eslintrc.json +0 -8
- package/lib/queryBuilder/parsers/jsonFieldExpressionParser.pegjs +0 -97
- package/lib/queryBuilder/parsers/relationExpressionParser.pegjs +0 -141
- package/lib/utils/clone.js +0 -2329
|
@@ -1,339 +1,291 @@
|
|
|
1
1
|
'use strict';
|
|
2
|
-
|
|
3
2
|
const { RawBuilder, normalizeRawArgs } = require('./RawBuilder');
|
|
4
3
|
const { ref } = require('./ReferenceBuilder');
|
|
5
4
|
const { expression } = require('./expressions');
|
|
6
5
|
const { asArray, asSingle, isNumber, isObject, isPlainObject } = require('../utils/objectUtils');
|
|
7
|
-
const { getDialect, isMySql } = require('../utils/knexUtils');
|
|
8
|
-
const {
|
|
9
|
-
|
|
6
|
+
const { getDialect, isMySql, isMariaDb } = require('../utils/knexUtils');
|
|
7
|
+
const { jsonCastsForDialect, JSON_CAST } = require('../utils/mysqlJsonPath');
|
|
8
|
+
const { FUNCTIONS, MARIADB_UNSUPPORTED, buildOrderBy } = require('./functions/mysql');
|
|
10
9
|
class FunctionBuilder extends RawBuilder {
|
|
11
|
-
|
|
12
|
-
|
|
13
|
-
|
|
14
|
-
|
|
15
|
-
|
|
16
|
-
|
|
17
|
-
|
|
18
|
-
|
|
19
|
-
|
|
20
|
-
|
|
21
|
-
|
|
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
|
-
);
|
|
10
|
+
constructor(sql, args) {
|
|
11
|
+
super(sql, args);
|
|
12
|
+
// The name of the `fn` helper of a MySQL only function.
|
|
13
|
+
this._mysqlFunction = null;
|
|
14
|
+
// Why the helper can't be used on MariaDB, if it can't.
|
|
15
|
+
this._mariaDbError = null;
|
|
16
|
+
// For aggregate functions, the number of the first `?`s that are aggregated,
|
|
17
|
+
// or '*' for `COUNT(*)`.
|
|
18
|
+
this._aggregated = null;
|
|
19
|
+
// The name of the `fn` helper of a window function.
|
|
20
|
+
this._windowFunction = null;
|
|
21
|
+
this._filters = [];
|
|
22
|
+
this._over = null;
|
|
35
23
|
}
|
|
36
|
-
|
|
37
|
-
|
|
38
|
-
|
|
39
|
-
|
|
40
|
-
|
|
41
|
-
|
|
42
|
-
|
|
43
|
-
|
|
44
|
-
|
|
45
|
-
|
|
46
|
-
|
|
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('filter() only works with the aggregate functions of fn, like fn.count() and fn.sum().');
|
|
33
|
+
}
|
|
34
|
+
this._filters.push(condition);
|
|
35
|
+
return this;
|
|
47
36
|
}
|
|
48
|
-
|
|
49
|
-
|
|
50
|
-
|
|
51
|
-
|
|
52
|
-
|
|
53
|
-
|
|
54
|
-
|
|
55
|
-
|
|
56
|
-
|
|
57
|
-
throw new Error(
|
|
58
|
-
`fn.${this._mysqlFunction}() is a MySQL function and can't be used with ${getDialect(knex)}.`,
|
|
59
|
-
);
|
|
37
|
+
// `fn.rowNumber().over({ partitionBy: 'ownerId', orderBy: ['createdAt', 'desc'] })`.
|
|
38
|
+
over(window = {}) {
|
|
39
|
+
for (const key of Object.keys(window)) {
|
|
40
|
+
if (key !== 'partitionBy' && key !== 'orderBy') {
|
|
41
|
+
throw new Error(`over(): unknown option "${key}". Use partitionBy and orderBy.`);
|
|
42
|
+
}
|
|
43
|
+
}
|
|
44
|
+
this._over = window;
|
|
45
|
+
return this;
|
|
60
46
|
}
|
|
61
|
-
|
|
62
|
-
|
|
63
|
-
|
|
47
|
+
toKnexRaw(builder) {
|
|
48
|
+
const knex = builder.unsafeKnex();
|
|
49
|
+
if (this._mysqlFunction && knex && !isMySql(knex)) {
|
|
50
|
+
throw new Error(`fn.${this._mysqlFunction}() is a MySQL function and can't be used with ${getDialect(knex)}.`);
|
|
51
|
+
}
|
|
52
|
+
// The SQL of the helpers is created before the database is known.
|
|
53
|
+
if (knex && isMariaDb(knex) && this._sql.includes(JSON_CAST)) {
|
|
54
|
+
const copy = Object.assign(Object.create(Object.getPrototypeOf(this)), this);
|
|
55
|
+
copy._sql = jsonCastsForDialect(this._sql, knex);
|
|
56
|
+
return copy.toKnexRaw(builder);
|
|
57
|
+
}
|
|
58
|
+
if (this._mariaDbError && knex && isMariaDb(knex)) {
|
|
59
|
+
throw new Error(this._mariaDbError);
|
|
60
|
+
}
|
|
61
|
+
if (this._windowFunction && !this._over) {
|
|
62
|
+
throw new Error(`fn.${this._windowFunction}() is a window function and needs over().`);
|
|
63
|
+
}
|
|
64
|
+
if (this._filters.length === 0 && !this._over) {
|
|
65
|
+
return super.toKnexRaw(builder);
|
|
66
|
+
}
|
|
67
|
+
return new RawBuilder(...this._buildFilterAndOver()).as(this._as).toKnexRaw(builder);
|
|
64
68
|
}
|
|
65
|
-
|
|
66
|
-
|
|
67
|
-
|
|
69
|
+
_buildFilterAndOver() {
|
|
70
|
+
let sql = this._sql;
|
|
71
|
+
let args = this._args;
|
|
72
|
+
if (this._filters.length !== 0) {
|
|
73
|
+
const condition = this._filters.length === 1
|
|
74
|
+
? this._filters[0]
|
|
75
|
+
: expression(`(${this._filters.map(() => '?').join(' AND ')})`, this._filters);
|
|
76
|
+
if (this._aggregated === '*') {
|
|
77
|
+
sql = sql.replace('*', 'CASE WHEN ? THEN 1 END');
|
|
78
|
+
args = [condition];
|
|
79
|
+
}
|
|
80
|
+
else {
|
|
81
|
+
const filtered = [];
|
|
82
|
+
let i = 0;
|
|
83
|
+
sql = sql.replace(/\?/g, () => {
|
|
84
|
+
const arg = args[i++];
|
|
85
|
+
if (i <= this._aggregated) {
|
|
86
|
+
filtered.push(condition, arg);
|
|
87
|
+
return 'CASE WHEN ? THEN ? END';
|
|
88
|
+
}
|
|
89
|
+
else {
|
|
90
|
+
filtered.push(arg);
|
|
91
|
+
return '?';
|
|
92
|
+
}
|
|
93
|
+
});
|
|
94
|
+
args = filtered;
|
|
95
|
+
}
|
|
96
|
+
}
|
|
97
|
+
if (this._over) {
|
|
98
|
+
const [overSql, overArgs] = buildOver(this._over);
|
|
99
|
+
sql = `${sql} OVER (${overSql})`;
|
|
100
|
+
args = [...args, ...overArgs];
|
|
101
|
+
}
|
|
102
|
+
return [sql, args];
|
|
68
103
|
}
|
|
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
104
|
}
|
|
115
|
-
|
|
116
105
|
// `fn.case()` is `CASE WHEN condition THEN result ... END` and `fn.case(value)` is
|
|
117
106
|
// `CASE value WHEN other THEN result ... END`.
|
|
118
107
|
class CaseBuilder extends FunctionBuilder {
|
|
119
|
-
|
|
120
|
-
|
|
121
|
-
|
|
122
|
-
|
|
123
|
-
|
|
124
|
-
|
|
125
|
-
|
|
126
|
-
|
|
127
|
-
|
|
128
|
-
|
|
129
|
-
|
|
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().');
|
|
108
|
+
constructor(hasValue, value) {
|
|
109
|
+
super('', []);
|
|
110
|
+
this._hasValue = hasValue;
|
|
111
|
+
this._value = value;
|
|
112
|
+
this._whens = [];
|
|
113
|
+
this._hasElse = false;
|
|
114
|
+
this._else = null;
|
|
115
|
+
}
|
|
116
|
+
when(condition, result) {
|
|
117
|
+
this._whens.push([condition, result]);
|
|
118
|
+
return this;
|
|
142
119
|
}
|
|
143
|
-
|
|
144
|
-
|
|
145
|
-
|
|
146
|
-
|
|
147
|
-
for (const [condition, result] of this._whens) {
|
|
148
|
-
sql += ' WHEN ? THEN ?';
|
|
149
|
-
args.push(condition, result);
|
|
120
|
+
else(result) {
|
|
121
|
+
this._hasElse = true;
|
|
122
|
+
this._else = result;
|
|
123
|
+
return this;
|
|
150
124
|
}
|
|
151
|
-
|
|
152
|
-
|
|
153
|
-
|
|
154
|
-
|
|
125
|
+
toKnexRaw(builder) {
|
|
126
|
+
if (this._whens.length === 0) {
|
|
127
|
+
throw new Error('fn.case() needs at least one when().');
|
|
128
|
+
}
|
|
129
|
+
const args = this._hasValue ? [this._value] : [];
|
|
130
|
+
let sql = this._hasValue ? '(CASE ?' : '(CASE';
|
|
131
|
+
for (const [condition, result] of this._whens) {
|
|
132
|
+
sql += ' WHEN ? THEN ?';
|
|
133
|
+
args.push(condition, result);
|
|
134
|
+
}
|
|
135
|
+
if (this._hasElse) {
|
|
136
|
+
sql += ' ELSE ?';
|
|
137
|
+
args.push(this._else);
|
|
138
|
+
}
|
|
139
|
+
this._sql = `${sql} END)`;
|
|
140
|
+
this._args = args;
|
|
141
|
+
return super.toKnexRaw(builder);
|
|
155
142
|
}
|
|
156
|
-
|
|
157
|
-
this._sql = `${sql} END)`;
|
|
158
|
-
this._args = args;
|
|
159
|
-
|
|
160
|
-
return super.toKnexRaw(builder);
|
|
161
|
-
}
|
|
162
143
|
}
|
|
163
|
-
|
|
164
144
|
// The value an upsert tried to insert: `VALUES(col)` on MySQL, `excluded.col` on
|
|
165
145
|
// postgres and SQLite.
|
|
166
146
|
class ExcludedBuilder extends FunctionBuilder {
|
|
167
|
-
|
|
168
|
-
|
|
169
|
-
|
|
170
|
-
|
|
171
|
-
|
|
147
|
+
constructor(column) {
|
|
148
|
+
super('', []);
|
|
149
|
+
if (isObject(column) && column.isObjectionReferenceBuilder) {
|
|
150
|
+
column = column.column;
|
|
151
|
+
}
|
|
152
|
+
if (typeof column !== 'string') {
|
|
153
|
+
throw new Error('fn.excluded() takes a column name.');
|
|
154
|
+
}
|
|
155
|
+
this._column = column;
|
|
172
156
|
}
|
|
173
|
-
|
|
174
|
-
|
|
175
|
-
|
|
157
|
+
toKnexRaw(builder) {
|
|
158
|
+
if (isMySql(builder.unsafeKnex())) {
|
|
159
|
+
this._sql = 'VALUES(??)';
|
|
160
|
+
this._args = [this._column];
|
|
161
|
+
}
|
|
162
|
+
else {
|
|
163
|
+
this._sql = '??';
|
|
164
|
+
this._args = [`excluded.${this._column}`];
|
|
165
|
+
}
|
|
166
|
+
return super.toKnexRaw(builder);
|
|
176
167
|
}
|
|
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
168
|
}
|
|
193
|
-
|
|
194
169
|
function buildOver({ partitionBy, orderBy }) {
|
|
195
|
-
|
|
196
|
-
|
|
197
|
-
|
|
198
|
-
|
|
199
|
-
|
|
200
|
-
|
|
201
|
-
|
|
202
|
-
|
|
203
|
-
|
|
204
|
-
|
|
205
|
-
|
|
206
|
-
|
|
207
|
-
|
|
208
|
-
}
|
|
209
|
-
|
|
210
|
-
return [parts.join(' '), args];
|
|
170
|
+
const parts = [];
|
|
171
|
+
const args = [];
|
|
172
|
+
if (partitionBy !== undefined) {
|
|
173
|
+
const columns = asArray(partitionBy).map((it) => (typeof it === 'string' ? ref(it) : it));
|
|
174
|
+
parts.push(`PARTITION BY ${columns.map(() => '?').join(', ')}`);
|
|
175
|
+
args.push(...columns);
|
|
176
|
+
}
|
|
177
|
+
if (orderBy !== undefined) {
|
|
178
|
+
const [orderBySql, orderByArgs] = buildOrderBy('over()', orderBy);
|
|
179
|
+
parts.push(`ORDER BY ${orderBySql}`);
|
|
180
|
+
args.push(...orderByArgs);
|
|
181
|
+
}
|
|
182
|
+
return [parts.join(' '), args];
|
|
211
183
|
}
|
|
212
|
-
|
|
213
184
|
function fn(...argsIn) {
|
|
214
|
-
|
|
215
|
-
|
|
185
|
+
const { sql, args } = normalizeRawArgs(argsIn);
|
|
186
|
+
return new FunctionBuilder(`${sql}(${args.map(() => '?').join(', ')})`, args);
|
|
216
187
|
}
|
|
217
|
-
|
|
218
188
|
for (const func of ['coalesce', 'concat', 'upper', 'lower']) {
|
|
219
|
-
|
|
189
|
+
fn[func] = (...args) => fn(func.toUpperCase(), args);
|
|
220
190
|
}
|
|
221
|
-
|
|
222
191
|
for (const func of ['sum', 'avg', 'min', 'max']) {
|
|
223
|
-
|
|
192
|
+
fn[func] = (...args) => aggregate(fn(func.toUpperCase(), args), args.length);
|
|
224
193
|
}
|
|
225
|
-
|
|
226
194
|
// `fn.count()` and `fn.count('*')` are `COUNT(*)`.
|
|
227
195
|
fn.count = (...args) => {
|
|
228
|
-
|
|
229
|
-
|
|
230
|
-
|
|
231
|
-
|
|
232
|
-
return aggregate(fn('COUNT', args), args.length);
|
|
196
|
+
if (args.length === 0 || (args.length === 1 && args[0] === '*')) {
|
|
197
|
+
return aggregate(new FunctionBuilder('COUNT(*)', []), '*');
|
|
198
|
+
}
|
|
199
|
+
return aggregate(fn('COUNT', args), args.length);
|
|
233
200
|
};
|
|
234
|
-
|
|
235
201
|
fn.now = (precision) => {
|
|
236
|
-
|
|
237
|
-
|
|
238
|
-
|
|
239
|
-
|
|
240
|
-
|
|
241
|
-
|
|
242
|
-
|
|
243
|
-
|
|
244
|
-
|
|
245
|
-
// injection here.
|
|
246
|
-
return new FunctionBuilder(`CURRENT_TIMESTAMP(${precision})`, []);
|
|
202
|
+
precision = parseInt(asSingle(precision), 10);
|
|
203
|
+
if (isNaN(precision) || !isNumber(precision)) {
|
|
204
|
+
precision = 6;
|
|
205
|
+
}
|
|
206
|
+
// We need to use a literal precision instead of a binding here
|
|
207
|
+
// for the CURRENT_TIMESTAMP to work. This is okay here since we
|
|
208
|
+
// make sure `precision` is a number. There's no chance of SQL
|
|
209
|
+
// injection here.
|
|
210
|
+
return new FunctionBuilder(`CURRENT_TIMESTAMP(${precision})`, []);
|
|
247
211
|
};
|
|
248
|
-
|
|
249
212
|
fn.case = (...args) => new CaseBuilder(args.length !== 0, args[0]);
|
|
250
|
-
|
|
251
213
|
fn.exists = (subquery) => {
|
|
252
|
-
|
|
253
|
-
|
|
254
|
-
|
|
255
|
-
|
|
256
|
-
return new FunctionBuilder('(EXISTS ?)', [subquery]);
|
|
214
|
+
if (!isObject(subquery) || !subquery.isObjectionQueryBuilderBase) {
|
|
215
|
+
throw new Error('fn.exists() takes a query.');
|
|
216
|
+
}
|
|
217
|
+
return new FunctionBuilder('(EXISTS ?)', [subquery]);
|
|
257
218
|
};
|
|
258
|
-
|
|
259
219
|
fn.excluded = (column) => new ExcludedBuilder(column);
|
|
260
|
-
|
|
261
220
|
for (const [name, sql, , , options = {}] of FUNCTIONS) {
|
|
262
|
-
|
|
263
|
-
|
|
264
|
-
|
|
265
|
-
|
|
266
|
-
|
|
267
|
-
|
|
268
|
-
|
|
221
|
+
// `defineProperty` since `fn.length` is a read-only property of functions.
|
|
222
|
+
Object.defineProperty(fn, name, {
|
|
223
|
+
configurable: true,
|
|
224
|
+
enumerable: true,
|
|
225
|
+
writable: true,
|
|
226
|
+
value: createMySqlFunction(name, sql, options),
|
|
227
|
+
});
|
|
269
228
|
}
|
|
270
|
-
|
|
271
229
|
function createMySqlFunction(name, sql, options) {
|
|
272
|
-
|
|
273
|
-
|
|
274
|
-
|
|
275
|
-
|
|
276
|
-
|
|
277
|
-
|
|
278
|
-
|
|
279
|
-
|
|
280
|
-
|
|
281
|
-
|
|
282
|
-
|
|
283
|
-
|
|
284
|
-
|
|
285
|
-
|
|
286
|
-
|
|
287
|
-
|
|
288
|
-
|
|
289
|
-
|
|
290
|
-
|
|
291
|
-
|
|
292
|
-
};
|
|
230
|
+
return (...args) => {
|
|
231
|
+
const [text, values] = options.build
|
|
232
|
+
? options.build(name, sql, args)
|
|
233
|
+
: [`${sql}(${args.map(() => '?').join(', ')})`, args];
|
|
234
|
+
const builder = createWithJsonArgs(text, values, options.json);
|
|
235
|
+
if (!options.portable) {
|
|
236
|
+
builder._mysqlFunction = name;
|
|
237
|
+
}
|
|
238
|
+
const mariaDbReason = MARIADB_UNSUPPORTED[name] && MARIADB_UNSUPPORTED[name](args);
|
|
239
|
+
if (mariaDbReason) {
|
|
240
|
+
builder._mariaDbError = `fn.${name}() ${mariaDbReason}.`;
|
|
241
|
+
}
|
|
242
|
+
if (options.window) {
|
|
243
|
+
builder._windowFunction = name;
|
|
244
|
+
}
|
|
245
|
+
if (options.aggregate) {
|
|
246
|
+
aggregate(builder, options.aggregate === true ? args.length : options.aggregate(args));
|
|
247
|
+
}
|
|
248
|
+
return builder;
|
|
249
|
+
};
|
|
293
250
|
}
|
|
294
|
-
|
|
295
251
|
function aggregate(builder, aggregated) {
|
|
296
|
-
|
|
297
|
-
|
|
252
|
+
builder._aggregated = aggregated;
|
|
253
|
+
return builder;
|
|
298
254
|
}
|
|
299
|
-
|
|
300
255
|
// Objects and arrays passed as JSON documents are cast to JSON. Without the cast
|
|
301
256
|
// MySQL would get a string (or knex would expand an array into a list).
|
|
302
257
|
function createWithJsonArgs(text, values, json) {
|
|
303
|
-
|
|
304
|
-
|
|
305
|
-
|
|
306
|
-
|
|
307
|
-
|
|
308
|
-
|
|
309
|
-
return new FunctionBuilder(text, values);
|
|
258
|
+
const casts = values.map((value, i) => isJsonArg(json, i) && isJsonValue(value));
|
|
259
|
+
let i = 0;
|
|
260
|
+
text = text.replace(/\?/g, () => (casts[i++] ? 'CAST(? AS JSON)' : '?'));
|
|
261
|
+
values = values.map((value, i) => (casts[i] ? JSON.stringify(value) : value));
|
|
262
|
+
return new FunctionBuilder(text, values);
|
|
310
263
|
}
|
|
311
|
-
|
|
312
264
|
function isJsonArg(json, i) {
|
|
313
|
-
|
|
314
|
-
|
|
315
|
-
|
|
316
|
-
|
|
317
|
-
|
|
318
|
-
|
|
319
|
-
|
|
320
|
-
|
|
321
|
-
|
|
265
|
+
if (json === 'all') {
|
|
266
|
+
return true;
|
|
267
|
+
}
|
|
268
|
+
else if (json === 'even') {
|
|
269
|
+
return i % 2 === 0;
|
|
270
|
+
}
|
|
271
|
+
else if (json === 'odd') {
|
|
272
|
+
return i % 2 === 1;
|
|
273
|
+
}
|
|
274
|
+
else {
|
|
275
|
+
return Array.isArray(json) && json.includes(i);
|
|
276
|
+
}
|
|
322
277
|
}
|
|
323
|
-
|
|
324
278
|
function isJsonValue(value) {
|
|
325
|
-
|
|
279
|
+
return Array.isArray(value) || isPlainObject(value);
|
|
326
280
|
}
|
|
327
|
-
|
|
328
281
|
Object.defineProperties(FunctionBuilder.prototype, {
|
|
329
|
-
|
|
330
|
-
|
|
331
|
-
|
|
332
|
-
|
|
333
|
-
|
|
282
|
+
isObjectionFunctionBuilder: {
|
|
283
|
+
enumerable: false,
|
|
284
|
+
writable: false,
|
|
285
|
+
value: true,
|
|
286
|
+
},
|
|
334
287
|
});
|
|
335
|
-
|
|
336
288
|
module.exports = {
|
|
337
|
-
|
|
338
|
-
|
|
289
|
+
FunctionBuilder,
|
|
290
|
+
fn,
|
|
339
291
|
};
|
|
@@ -1,29 +1,24 @@
|
|
|
1
1
|
'use strict';
|
|
2
|
-
|
|
3
2
|
class InternalOptions {
|
|
4
|
-
|
|
5
|
-
|
|
6
|
-
|
|
7
|
-
|
|
8
|
-
|
|
9
|
-
|
|
10
|
-
|
|
11
|
-
|
|
12
|
-
|
|
13
|
-
|
|
14
|
-
|
|
15
|
-
|
|
16
|
-
|
|
17
|
-
|
|
18
|
-
|
|
19
|
-
|
|
20
|
-
|
|
21
|
-
|
|
22
|
-
|
|
23
|
-
return copy;
|
|
24
|
-
}
|
|
3
|
+
constructor() {
|
|
4
|
+
this.skipUndefined = false;
|
|
5
|
+
this.keepImplicitJoinProps = false;
|
|
6
|
+
this.returnImmediatelyValue = undefined;
|
|
7
|
+
this.isInternalQuery = false;
|
|
8
|
+
this.debug = false;
|
|
9
|
+
this.schema = undefined;
|
|
10
|
+
}
|
|
11
|
+
clone() {
|
|
12
|
+
const copy = new this.constructor();
|
|
13
|
+
copy.skipUndefined = this.skipUndefined;
|
|
14
|
+
copy.keepImplicitJoinProps = this.keepImplicitJoinProps;
|
|
15
|
+
copy.returnImmediatelyValue = this.returnImmediatelyValue;
|
|
16
|
+
copy.isInternalQuery = this.isInternalQuery;
|
|
17
|
+
copy.debug = this.debug;
|
|
18
|
+
copy.schema = this.schema;
|
|
19
|
+
return copy;
|
|
20
|
+
}
|
|
25
21
|
}
|
|
26
|
-
|
|
27
22
|
module.exports = {
|
|
28
|
-
|
|
23
|
+
InternalOptions,
|
|
29
24
|
};
|