objection 3.9.0 → 4.0.0-rc.1
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/lib/dbErrors/LICENSE +21 -0
- package/lib/dbErrors/errorCodes/mssql.js +19 -0
- package/lib/dbErrors/errorCodes/mysql.js +805 -0
- package/lib/dbErrors/errorCodes/postgres.js +241 -0
- package/lib/dbErrors/errors/CheckViolationError.js +10 -0
- package/lib/dbErrors/errors/ConstraintViolationError.js +3 -0
- package/lib/dbErrors/errors/DBError.js +9 -0
- package/lib/dbErrors/errors/DataError.js +3 -0
- package/lib/dbErrors/errors/ForeignKeyViolationError.js +11 -0
- package/lib/dbErrors/errors/NotNullViolationError.js +12 -0
- package/lib/dbErrors/errors/UniqueViolationError.js +12 -0
- package/lib/dbErrors/index.js +59 -0
- package/lib/dbErrors/parsers/mssql/DBError/CheckViolationError/parser.js +28 -0
- package/lib/dbErrors/parsers/mssql/DBError/DataError/parser.js +25 -0
- package/lib/dbErrors/parsers/mssql/DBError/ForeignKeyViolationError/parser.js +39 -0
- package/lib/dbErrors/parsers/mssql/DBError/NotNullViolationError/parser.js +30 -0
- package/lib/dbErrors/parsers/mssql/DBError/UniqueViolationError/parser.js +53 -0
- package/lib/dbErrors/parsers/mssql/DBError/parser.js +51 -0
- package/lib/dbErrors/parsers/mssql/DBError/util.js +16 -0
- package/lib/dbErrors/parsers/mysql/DBError/ConstraintViolationError/ForeignKeyViolationError/parser.js +43 -0
- package/lib/dbErrors/parsers/mysql/DBError/ConstraintViolationError/NotNullViolationError/parser.js +29 -0
- package/lib/dbErrors/parsers/mysql/DBError/ConstraintViolationError/UniqueViolationError/parser.js +27 -0
- package/lib/dbErrors/parsers/mysql/DBError/ConstraintViolationError/parser.js +23 -0
- package/lib/dbErrors/parsers/mysql/DBError/DataError/parser.js +24 -0
- package/lib/dbErrors/parsers/mysql/DBError/parser.js +28 -0
- package/lib/dbErrors/parsers/postgres/DBError/ConstraintViolationError/CheckViolationError/parser.js +18 -0
- package/lib/dbErrors/parsers/postgres/DBError/ConstraintViolationError/ForeignKeyViolationError/parser.js +18 -0
- package/lib/dbErrors/parsers/postgres/DBError/ConstraintViolationError/NotNullViolationError/parser.js +18 -0
- package/lib/dbErrors/parsers/postgres/DBError/ConstraintViolationError/UniqueViolationError/parser.js +40 -0
- package/lib/dbErrors/parsers/postgres/DBError/ConstraintViolationError/parser.js +25 -0
- package/lib/dbErrors/parsers/postgres/DBError/DataError/parser.js +16 -0
- package/lib/dbErrors/parsers/postgres/DBError/parser.js +27 -0
- package/lib/dbErrors/parsers/sqlite/DBError/ConstraintViolationError/CheckViolationError/parser.js +20 -0
- package/lib/dbErrors/parsers/sqlite/DBError/ConstraintViolationError/ForeignKeyViolationError/parser.js +20 -0
- package/lib/dbErrors/parsers/sqlite/DBError/ConstraintViolationError/NotNullViolationError/parser.js +27 -0
- package/lib/dbErrors/parsers/sqlite/DBError/ConstraintViolationError/UniqueViolationError/parser.js +28 -0
- package/lib/dbErrors/parsers/sqlite/DBError/ConstraintViolationError/parser.js +24 -0
- package/lib/dbErrors/parsers/sqlite/DBError/parser.js +23 -0
- package/lib/dbErrors/utils/sqlState.js +3 -0
- package/lib/initialize.js +1 -7
- package/lib/model/AjvValidator.js +51 -117
- package/lib/model/Model.js +84 -59
- package/lib/model/ModifierNotFoundError.js +1 -7
- package/lib/model/NotFoundError.js +1 -7
- package/lib/model/RelationDoesNotExistError.js +1 -7
- package/lib/model/ValidationError.js +3 -10
- package/lib/model/Validator.js +1 -7
- package/lib/model/getModel.js +7 -6
- package/lib/model/inheritModel.js +1 -7
- package/lib/model/modelBindKnex.js +4 -10
- package/lib/model/modelClone.js +5 -11
- package/lib/model/modelColPropMap.js +3 -10
- package/lib/model/modelId.js +3 -10
- package/lib/model/modelJsonAttributes.js +35 -16
- package/lib/model/modelParseRelations.js +2 -8
- package/lib/model/modelQueryProps.js +43 -27
- package/lib/model/modelSet.js +89 -41
- package/lib/model/modelTableMetadata.js +4 -11
- package/lib/model/modelToJson.js +8 -23
- package/lib/model/modelUtils.js +14 -13
- package/lib/model/modelValidate.js +3 -9
- package/lib/model/modelValues.js +5 -14
- package/lib/model/modelVisitor.js +1 -7
- package/lib/objection.js +33 -71
- package/lib/queryBuilder/FunctionBuilder.js +4 -11
- package/lib/queryBuilder/InternalOptions.js +4 -9
- package/lib/queryBuilder/JoinBuilder.js +5 -8
- package/lib/queryBuilder/QueryBuilder.js +126 -95
- package/lib/queryBuilder/QueryBuilderBase.js +41 -123
- package/lib/queryBuilder/QueryBuilderContext.js +2 -8
- package/lib/queryBuilder/QueryBuilderContextBase.js +2 -8
- package/lib/queryBuilder/QueryBuilderOperationSupport.js +7 -21
- package/lib/queryBuilder/QueryBuilderUserContext.js +1 -7
- package/lib/queryBuilder/RawBuilder.js +5 -13
- package/lib/queryBuilder/ReferenceBuilder.js +20 -48
- package/lib/queryBuilder/RelationExpression.js +18 -16
- package/lib/queryBuilder/StaticHookArguments.js +74 -16
- package/lib/queryBuilder/ValueBuilder.js +11 -13
- package/lib/queryBuilder/getJoinBuilder.js +10 -0
- package/lib/queryBuilder/graph/GraphLoader.js +304 -0
- package/lib/queryBuilder/graph/GraphNode.js +235 -0
- package/lib/queryBuilder/graph/GraphOptions.js +55 -248
- package/lib/queryBuilder/graph/GraphPlan.js +395 -0
- package/lib/queryBuilder/graph/GraphUpsert.js +466 -410
- package/lib/queryBuilder/graph/GraphWriter.js +1263 -0
- package/lib/queryBuilder/join/JoinResultColumn.js +1 -7
- package/lib/queryBuilder/join/JoinResultParser.js +6 -11
- package/lib/queryBuilder/join/RelationJoiner.js +66 -84
- package/lib/queryBuilder/join/TableNode.js +2 -8
- package/lib/queryBuilder/join/TableTree.js +3 -9
- package/lib/queryBuilder/join/utils.js +5 -15
- package/lib/queryBuilder/jsonDialects/JsonDialect.js +51 -0
- package/lib/queryBuilder/jsonDialects/MySqlJsonDialect.js +81 -0
- package/lib/queryBuilder/jsonDialects/PostgresJsonDialect.js +45 -0
- package/lib/queryBuilder/jsonDialects/SqliteJsonDialect.js +67 -0
- package/lib/queryBuilder/jsonDialects/index.js +44 -0
- package/lib/queryBuilder/operations/DelegateOperation.js +2 -8
- package/lib/queryBuilder/operations/DeleteOperation.js +13 -35
- package/lib/queryBuilder/operations/FindByIdOperation.js +7 -12
- package/lib/queryBuilder/operations/FindByIdsOperation.js +4 -8
- package/lib/queryBuilder/operations/FindOperation.js +14 -34
- package/lib/queryBuilder/operations/FirstOperation.js +6 -10
- package/lib/queryBuilder/operations/FromOperation.js +3 -9
- package/lib/queryBuilder/operations/InsertAndFetchOperation.js +13 -12
- package/lib/queryBuilder/operations/InsertGraphAndFetchOperation.js +4 -10
- package/lib/queryBuilder/operations/InsertGraphOperation.js +7 -18
- package/lib/queryBuilder/operations/InsertOperation.js +118 -43
- package/lib/queryBuilder/operations/InstanceDeleteOperation.js +5 -12
- package/lib/queryBuilder/operations/InstanceFindOperation.js +4 -11
- package/lib/queryBuilder/operations/InstanceInsertOperation.js +2 -8
- package/lib/queryBuilder/operations/InstanceUpdateOperation.js +7 -14
- package/lib/queryBuilder/operations/JoinRelatedOperation.js +5 -11
- package/lib/queryBuilder/operations/KnexOperation.js +2 -8
- package/lib/queryBuilder/operations/LimitPerParentOperation.js +7 -69
- package/lib/queryBuilder/operations/MergeOperation.js +5 -11
- package/lib/queryBuilder/operations/NoneOperation.js +2 -8
- package/lib/queryBuilder/operations/ObjectionToKnexConvertingOperation.js +20 -49
- package/lib/queryBuilder/operations/OnBuildKnexOperation.js +2 -8
- package/lib/queryBuilder/operations/OnBuildOperation.js +2 -8
- package/lib/queryBuilder/operations/OnErrorOperation.js +2 -8
- package/lib/queryBuilder/operations/QueryBuilderOperation.js +1 -7
- package/lib/queryBuilder/operations/RangeOperation.js +11 -26
- package/lib/queryBuilder/operations/RelateOperation.js +2 -8
- package/lib/queryBuilder/operations/ReturningOperation.js +3 -9
- package/lib/queryBuilder/operations/RunAfterOperation.js +2 -12
- package/lib/queryBuilder/operations/RunBeforeOperation.js +2 -8
- package/lib/queryBuilder/operations/UnrelateOperation.js +2 -8
- package/lib/queryBuilder/operations/UpdateAndFetchOperation.js +14 -17
- package/lib/queryBuilder/operations/UpdateOperation.js +39 -125
- package/lib/queryBuilder/operations/UpsertGraphAndFetchOperation.js +8 -11
- package/lib/queryBuilder/operations/UpsertGraphOperation.js +6 -12
- package/lib/queryBuilder/operations/WhereCompositeOperation.js +3 -9
- package/lib/queryBuilder/operations/WhereGroupOperation.js +2 -8
- package/lib/queryBuilder/operations/eager/EagerOperation.js +3 -9
- package/lib/queryBuilder/operations/eager/JoinEagerOperation.js +72 -10
- package/lib/queryBuilder/operations/eager/NaiveEagerOperation.js +2 -8
- package/lib/queryBuilder/operations/eager/WhereInEagerOperation.js +23 -44
- package/lib/queryBuilder/operations/fieldExpressions.js +122 -0
- package/lib/queryBuilder/operations/jsonApi/WhereJsonHasOperation.js +20 -0
- package/lib/queryBuilder/operations/jsonApi/WhereJsonOperation.js +44 -0
- package/lib/queryBuilder/operations/jsonApi/WhereJsonTypeOperation.js +14 -0
- package/lib/queryBuilder/operations/select/SelectOperation.js +11 -21
- package/lib/queryBuilder/operations/select/Selection.js +19 -17
- package/lib/queryBuilder/operations/whereInComposite/WhereInCompositeMsSqlOperation.js +4 -10
- package/lib/queryBuilder/operations/whereInComposite/WhereInCompositeOperation.js +4 -10
- package/lib/queryBuilder/operations/whereInComposite/WhereInCompositeSqliteOperation.js +4 -10
- package/lib/queryBuilder/operations/windowOrder.js +56 -0
- package/lib/queryBuilder/parsers/jsonFieldExpressionParser.js +3 -7
- package/lib/queryBuilder/parsers/relationExpressionParser.js +42 -43
- package/lib/queryBuilder/parsers/relationExpressionParser.pegjs +39 -36
- package/lib/queryBuilder/transformations/CompositeQueryTransformation.js +2 -8
- package/lib/queryBuilder/transformations/QueryTransformation.js +1 -7
- package/lib/queryBuilder/transformations/WrapMysqlModifySubqueryTransformation.js +3 -9
- package/lib/queryBuilder/transformations/index.js +3 -11
- package/lib/queryBuilder/whereGroups.js +9 -24
- package/lib/queryBuilder/withoutOrder.js +4 -11
- package/lib/relations/Relation.js +47 -28
- package/lib/relations/RelationDeleteOperation.js +4 -14
- package/lib/relations/RelationFindOperation.js +119 -90
- package/lib/relations/RelationInsertOperation.js +2 -8
- package/lib/relations/RelationOwner.js +5 -11
- package/lib/relations/RelationProperty.js +4 -10
- package/lib/relations/RelationUpdateOperation.js +4 -14
- package/lib/relations/RelationWriteResultOperation.js +15 -0
- package/lib/relations/belongsToMany/BelongsToManyDeleteOperation.js +23 -0
- package/lib/relations/belongsToMany/BelongsToManyFindOperation.js +35 -0
- package/lib/relations/belongsToMany/BelongsToManyInsertOperation.js +28 -0
- package/lib/relations/belongsToMany/BelongsToManyRelateOperation.js +16 -0
- package/lib/relations/belongsToMany/BelongsToManyRelation.js +276 -0
- package/lib/relations/belongsToMany/BelongsToManyUnrelateOperation.js +11 -0
- package/lib/relations/belongsToOne/BelongsToOneDeleteOperation.js +2 -8
- package/lib/relations/belongsToOne/BelongsToOneInsertOperation.js +6 -11
- package/lib/relations/belongsToOne/BelongsToOneRelateOperation.js +3 -9
- package/lib/relations/belongsToOne/BelongsToOneRelation.js +8 -19
- package/lib/relations/belongsToOne/BelongsToOneUnrelateOperation.js +53 -27
- package/lib/relations/hasMany/HasManyInsertOperation.js +8 -9
- package/lib/relations/hasMany/HasManyRelateOperation.js +5 -11
- package/lib/relations/hasMany/HasManyRelation.js +9 -11
- package/lib/relations/hasMany/HasManyUnrelateOperation.js +3 -9
- package/lib/relations/hasOne/HasOneRelation.js +2 -8
- package/lib/relations/hasOneThrough/HasOneThroughRelation.js +2 -8
- package/lib/relations/manyToMany/ManyToManyModifyMixin.js +10 -15
- package/lib/relations/manyToMany/ManyToManyMySqlModifyMixin.js +2 -8
- package/lib/relations/manyToMany/ManyToManyRelation.js +82 -50
- package/lib/relations/manyToMany/ManyToManySqliteModifyMixin.js +2 -8
- package/lib/relations/manyToMany/delete/ManyToManyDeleteOperation.js +5 -9
- package/lib/relations/manyToMany/delete/ManyToManyDeleteOperationBase.js +2 -8
- package/lib/relations/manyToMany/delete/ManyToManyDeleteSqliteOperation.js +3 -9
- package/lib/relations/manyToMany/find/ManyToManyFindOperation.js +20 -59
- package/lib/relations/manyToMany/insert/ManyToManyInsertOperation.js +6 -11
- package/lib/relations/manyToMany/relate/ManyToManyRelateOperation.js +4 -10
- package/lib/relations/manyToMany/unrelate/ManyToManyUnrelateMySqlOperation.js +3 -9
- package/lib/relations/manyToMany/unrelate/ManyToManyUnrelateOperation.js +5 -9
- package/lib/relations/manyToMany/unrelate/ManyToManyUnrelateOperationBase.js +3 -9
- package/lib/relations/manyToMany/unrelate/ManyToManyUnrelateSqliteOperation.js +3 -9
- package/lib/relations/manyToMany/update/ManyToManyUpdateMySqlOperation.js +3 -9
- package/lib/relations/manyToMany/update/ManyToManyUpdateOperation.js +5 -9
- package/lib/relations/manyToMany/update/ManyToManyUpdateOperationBase.js +79 -64
- package/lib/relations/manyToMany/update/ManyToManyUpdateSqliteOperation.js +3 -9
- package/lib/transaction.js +3 -9
- package/lib/utils/assert.js +3 -11
- package/lib/utils/buildUtils.js +2 -8
- package/lib/utils/classUtils.js +1 -7
- package/lib/utils/createModifier.js +2 -8
- package/lib/utils/identifierMapping.js +23 -27
- package/lib/utils/internalPropUtils.js +1 -7
- package/lib/utils/knexUtils.js +84 -40
- package/lib/utils/mixin.js +3 -10
- package/lib/utils/normalizeIds.js +2 -8
- package/lib/utils/objectUtils.js +41 -60
- package/lib/utils/parseFieldExpression.js +29 -14
- package/lib/utils/promiseUtils/after.js +2 -8
- package/lib/utils/promiseUtils/afterReturn.js +2 -8
- package/lib/utils/promiseUtils/index.js +7 -16
- package/lib/utils/promiseUtils/isPromise.js +2 -8
- package/lib/utils/promiseUtils/map.js +25 -14
- package/lib/utils/promiseUtils/mapAfterAllReturn.js +2 -8
- package/lib/utils/promiseUtils/try.js +2 -8
- package/lib/utils/resolveModel.js +10 -17
- package/lib/utils/tmpColumnUtils.js +2 -9
- package/lib/utils/warnings.js +2 -14
- package/package.json +25 -17
- package/typings/objection/index.d.ts +3217 -2179
- package/lib/.eslintrc.json +0 -8
- package/lib/model/graph/ModelGraph.js +0 -80
- package/lib/model/graph/ModelGraphBuilder.js +0 -241
- package/lib/model/graph/ModelGraphEdge.js +0 -52
- package/lib/model/graph/ModelGraphNode.js +0 -253
- package/lib/queryBuilder/graph/GraphAction.js +0 -69
- package/lib/queryBuilder/graph/GraphData.js +0 -53
- package/lib/queryBuilder/graph/GraphFetcher.js +0 -199
- package/lib/queryBuilder/graph/GraphNodeDbExistence.js +0 -184
- package/lib/queryBuilder/graph/GraphOperation.js +0 -27
- package/lib/queryBuilder/graph/delete/GraphDelete.js +0 -73
- package/lib/queryBuilder/graph/delete/GraphDeleteAction.js +0 -88
- package/lib/queryBuilder/graph/insert/DeferredRelationGraphInsertAction.js +0 -57
- package/lib/queryBuilder/graph/insert/GraphInsert.js +0 -257
- package/lib/queryBuilder/graph/insert/GraphInsertAction.js +0 -238
- package/lib/queryBuilder/graph/insert/JoinRowGraphInsertAction.js +0 -92
- package/lib/queryBuilder/graph/patch/GraphPatch.js +0 -20
- package/lib/queryBuilder/graph/patch/GraphPatchAction.js +0 -277
- package/lib/queryBuilder/graph/recursiveUpsert/GraphRecursiveUpsert.js +0 -31
- package/lib/queryBuilder/graph/recursiveUpsert/GraphRecursiveUpsertAction.js +0 -126
- package/lib/queryBuilder/operations/jsonApi/WhereJsonHasPostgresOperation.js +0 -30
- package/lib/queryBuilder/operations/jsonApi/WhereJsonNotObjectPostgresOperation.js +0 -37
- package/lib/queryBuilder/operations/jsonApi/WhereJsonPostgresOperation.js +0 -62
- package/lib/queryBuilder/operations/jsonApi/postgresJsonApi.js +0 -192
- package/lib/utils/wrapDbError.js +0 -50
|
@@ -1,6 +1,4 @@
|
|
|
1
|
-
|
|
2
|
-
|
|
3
|
-
class JoinResultColumn {
|
|
1
|
+
export class JoinResultColumn {
|
|
4
2
|
constructor({ columnAlias, tableNode, name }) {
|
|
5
3
|
this.columnAlias = columnAlias;
|
|
6
4
|
this.tableNode = tableNode;
|
|
@@ -17,7 +15,3 @@ class JoinResultColumn {
|
|
|
17
15
|
});
|
|
18
16
|
}
|
|
19
17
|
}
|
|
20
|
-
|
|
21
|
-
module.exports = {
|
|
22
|
-
JoinResultColumn,
|
|
23
|
-
};
|
|
@@ -1,11 +1,10 @@
|
|
|
1
|
-
|
|
1
|
+
import { JoinResultColumn } from './JoinResultColumn.js';
|
|
2
|
+
import { groupBy } from '../../utils/objectUtils.js';
|
|
2
3
|
|
|
3
|
-
|
|
4
|
-
|
|
5
|
-
|
|
6
|
-
class JoinResultParser {
|
|
7
|
-
constructor({ tableTree, omitColumnAliases = [] }) {
|
|
4
|
+
export class JoinResultParser {
|
|
5
|
+
constructor({ tableTree, knex, omitColumnAliases = [] }) {
|
|
8
6
|
this.tableTree = tableTree;
|
|
7
|
+
this.modelOptions = { knex };
|
|
9
8
|
this.omitColumnAliases = new Set(omitColumnAliases);
|
|
10
9
|
|
|
11
10
|
this.columnsByTableNode = null;
|
|
@@ -71,7 +70,7 @@ class JoinResultParser {
|
|
|
71
70
|
}
|
|
72
71
|
}
|
|
73
72
|
|
|
74
|
-
const model = tableNode.modelClass.fromDatabaseJson(row);
|
|
73
|
+
const model = tableNode.modelClass.fromDatabaseJson(row, this.modelOptions);
|
|
75
74
|
|
|
76
75
|
for (const childNode of tableNode.childNodes) {
|
|
77
76
|
if (childNode.relation.isOneToOne()) {
|
|
@@ -140,7 +139,3 @@ function getKey(parentKey, id) {
|
|
|
140
139
|
return escapedId;
|
|
141
140
|
}
|
|
142
141
|
}
|
|
143
|
-
|
|
144
|
-
module.exports = {
|
|
145
|
-
JoinResultParser,
|
|
146
|
-
};
|
|
@@ -1,20 +1,18 @@
|
|
|
1
|
-
|
|
1
|
+
import { uniqBy } from '../../utils/objectUtils.js';
|
|
2
|
+
import { Selection, isSameTable, mapIdentifier } from '../operations/select/Selection.js';
|
|
3
|
+
import { SelectOperation } from '../operations/select/SelectOperation.js';
|
|
4
|
+
import { createModifier } from '../../utils/createModifier.js';
|
|
5
|
+
import { map as mapPromise } from '../../utils/promiseUtils/index.js';
|
|
6
|
+
import { ValidationErrorType } from '../../model/ValidationError.js';
|
|
2
7
|
|
|
3
|
-
|
|
4
|
-
|
|
5
|
-
|
|
6
|
-
const { createModifier } = require('../../utils/createModifier');
|
|
7
|
-
const { map: mapPromise } = require('../../utils/promiseUtils');
|
|
8
|
-
const { ValidationErrorType } = require('../../model/ValidationError');
|
|
9
|
-
|
|
10
|
-
const { TableTree } = require('./TableTree');
|
|
11
|
-
const { JoinResultParser } = require('./JoinResultParser');
|
|
12
|
-
const { getIdentifierLengthLimit } = require('./utils');
|
|
8
|
+
import { TableTree } from './TableTree.js';
|
|
9
|
+
import { JoinResultParser } from './JoinResultParser.js';
|
|
10
|
+
import { getIdentifierLengthLimit } from './utils.js';
|
|
13
11
|
|
|
14
12
|
/**
|
|
15
13
|
* Given a relation expression, builds a query using joins to fetch it.
|
|
16
14
|
*/
|
|
17
|
-
class RelationJoiner {
|
|
15
|
+
export class RelationJoiner {
|
|
18
16
|
constructor({ modelClass }) {
|
|
19
17
|
this.rootModelClass = modelClass;
|
|
20
18
|
|
|
@@ -99,6 +97,7 @@ class RelationJoiner {
|
|
|
99
97
|
parseResult(builder, flatRows) {
|
|
100
98
|
const parser = JoinResultParser.create({
|
|
101
99
|
tableTree: this._getTableTree(builder),
|
|
100
|
+
knex: builder.knex(),
|
|
102
101
|
omitColumnAliases: this.internalSelections.map((it) => it.alias),
|
|
103
102
|
});
|
|
104
103
|
|
|
@@ -132,7 +131,7 @@ class RelationJoiner {
|
|
|
132
131
|
// also contain selects.
|
|
133
132
|
userSelectQuery.modify(tableNode.relation.modify);
|
|
134
133
|
|
|
135
|
-
if (selectsOnlyAliasesKeepingSelectAll(
|
|
134
|
+
if (selectsOnlyAliasesKeepingSelectAll(userSelectQuery)) {
|
|
136
135
|
// All columns are selected from the joined subquery in the main query,
|
|
137
136
|
// so the subquery needs to select them too.
|
|
138
137
|
subqueryToJoin.select(`${subqueryToJoin.tableRef()}.*`);
|
|
@@ -142,8 +141,23 @@ class RelationJoiner {
|
|
|
142
141
|
// for later use.
|
|
143
142
|
userSelectQueries.set(tableNode, userSelectQuery);
|
|
144
143
|
|
|
144
|
+
const path = getRelationPath(tableNode);
|
|
145
|
+
// The `modifyJoin()` modifiers whose path ends at this relation:
|
|
146
|
+
const joinModifiers = builder
|
|
147
|
+
.joinModifiersAtPath()
|
|
148
|
+
.filter((it) => getExpressionAtPath(it.path, path)?.isEmpty)
|
|
149
|
+
.map((it) => it.modifier);
|
|
150
|
+
|
|
145
151
|
tableNode.relation.join(builder, {
|
|
146
|
-
joinOperation: this._getJoinOperation(
|
|
152
|
+
joinOperation: this._getJoinOperation(path),
|
|
153
|
+
modifyJoin:
|
|
154
|
+
joinModifiers.length > 0
|
|
155
|
+
? (join, aliases) => {
|
|
156
|
+
for (const modifier of joinModifiers) {
|
|
157
|
+
modifier(join, aliases);
|
|
158
|
+
}
|
|
159
|
+
}
|
|
160
|
+
: undefined,
|
|
147
161
|
|
|
148
162
|
ownerTable: tableNode.parentNode.alias,
|
|
149
163
|
relatedTableAlias: tableNode.alias,
|
|
@@ -161,18 +175,12 @@ class RelationJoiner {
|
|
|
161
175
|
}
|
|
162
176
|
|
|
163
177
|
// Returns the `joinOperation` of the last `withGraphJoined` call that
|
|
164
|
-
// contains the relation
|
|
165
|
-
_getJoinOperation(
|
|
166
|
-
const path = [];
|
|
167
|
-
|
|
168
|
-
for (let node = tableNode; node.parentNode; node = node.parentNode) {
|
|
169
|
-
path.unshift(node.relationProperty);
|
|
170
|
-
}
|
|
171
|
-
|
|
178
|
+
// contains the relation path, falling back to the default.
|
|
179
|
+
_getJoinOperation(path) {
|
|
172
180
|
for (let i = this.joinOperations.length - 1; i >= 0; --i) {
|
|
173
181
|
const { expression, joinOperation } = this.joinOperations[i];
|
|
174
182
|
|
|
175
|
-
if (
|
|
183
|
+
if (getExpressionAtPath(expression, path)) {
|
|
176
184
|
return joinOperation;
|
|
177
185
|
}
|
|
178
186
|
}
|
|
@@ -216,7 +224,7 @@ class RelationJoiner {
|
|
|
216
224
|
const tableTree = this._getTableTree(builder);
|
|
217
225
|
const userSelectionsByName = new Map();
|
|
218
226
|
|
|
219
|
-
for (const selection of
|
|
227
|
+
for (const selection of builder.findAllSelections()) {
|
|
220
228
|
// Only the columns of the joined tables are known for `table.*`:
|
|
221
229
|
const node = isSelectAll(selection) ? tableTree.nodesByAlias.get(selection.table) : null;
|
|
222
230
|
const names = node
|
|
@@ -276,16 +284,26 @@ function defaultOptions() {
|
|
|
276
284
|
};
|
|
277
285
|
}
|
|
278
286
|
|
|
279
|
-
function
|
|
287
|
+
function getRelationPath(tableNode) {
|
|
288
|
+
const path = [];
|
|
289
|
+
|
|
290
|
+
for (let node = tableNode; node.parentNode; node = node.parentNode) {
|
|
291
|
+
path.unshift(node.relationProperty);
|
|
292
|
+
}
|
|
293
|
+
|
|
294
|
+
return path;
|
|
295
|
+
}
|
|
296
|
+
|
|
297
|
+
function getExpressionAtPath(expression, path) {
|
|
280
298
|
for (const name of path) {
|
|
281
299
|
expression = expression.childExpression(name);
|
|
282
300
|
|
|
283
301
|
if (!expression) {
|
|
284
|
-
return
|
|
302
|
+
return null;
|
|
285
303
|
}
|
|
286
304
|
}
|
|
287
305
|
|
|
288
|
-
return
|
|
306
|
+
return expression;
|
|
289
307
|
}
|
|
290
308
|
|
|
291
309
|
function createSubqueryToJoin({ builder, tableNode, modifiers }) {
|
|
@@ -328,9 +346,8 @@ function ensureIdAndRelationPropsAreSelected({ builder, tableNode }) {
|
|
|
328
346
|
),
|
|
329
347
|
];
|
|
330
348
|
|
|
331
|
-
const hasSelectionAs = createHasSelectionAs(builder);
|
|
332
349
|
const selectStrings = uniqBy(cols)
|
|
333
|
-
.filter((col) => !hasSelectionAs(col))
|
|
350
|
+
.filter((col) => !builder.hasSelectionAs(col, col))
|
|
334
351
|
.map((col) => `${tableRef}.${col}`);
|
|
335
352
|
|
|
336
353
|
return builder.select(selectStrings);
|
|
@@ -338,8 +355,8 @@ function ensureIdAndRelationPropsAreSelected({ builder, tableNode }) {
|
|
|
338
355
|
|
|
339
356
|
function getSelectionsForNode({ builder, tableNode, userSelectQueries }) {
|
|
340
357
|
const userSelectQuery = userSelectQueries.get(tableNode);
|
|
341
|
-
const userSelections =
|
|
342
|
-
const userSelectedAllColumns = isSelectAllSelectionSet(
|
|
358
|
+
const userSelections = userSelectQuery.findAllSelections();
|
|
359
|
+
const userSelectedAllColumns = isSelectAllSelectionSet(userSelectQuery);
|
|
343
360
|
|
|
344
361
|
let selections = [];
|
|
345
362
|
let internalSelections = [];
|
|
@@ -381,57 +398,15 @@ function getSelectionsForNode({ builder, tableNode, userSelectQueries }) {
|
|
|
381
398
|
};
|
|
382
399
|
}
|
|
383
400
|
|
|
384
|
-
// Like `builder.findAllSelections()`, but aggregates select their aliases
|
|
385
|
-
// instead of the columns they're passed. Aggregates without an alias are left
|
|
386
|
-
// out, as their column name differs between databases.
|
|
387
|
-
function findJoinSelections(builder) {
|
|
388
|
-
let selections = [];
|
|
389
|
-
|
|
390
|
-
builder.forEachOperation(true, (op) => {
|
|
391
|
-
if (op.constructor === SelectOperation) {
|
|
392
|
-
selections = selections.concat(op.aggregateSelections || op.selections);
|
|
393
|
-
}
|
|
394
|
-
});
|
|
395
|
-
|
|
396
|
-
return selections;
|
|
397
|
-
}
|
|
398
|
-
|
|
399
|
-
// Create a `(column) => builder.hasSelectionAs(column, column)` function that
|
|
400
|
-
// ignores the columns passed to aggregates, as they don't select them. Clone
|
|
401
|
-
// the builder once, not once per column.
|
|
402
|
-
function createHasSelectionAs(builder) {
|
|
403
|
-
if (builder.has(isAggregateOperation)) {
|
|
404
|
-
const selectionQuery = builder.clone().clear(isAggregateOperation);
|
|
405
|
-
// Explicit, as a query with only aggregates doesn't select all columns.
|
|
406
|
-
return (column) => selectionQuery.hasSelectionAs(column, column, true);
|
|
407
|
-
} else {
|
|
408
|
-
return (column) => builder.hasSelectionAs(column, column);
|
|
409
|
-
}
|
|
410
|
-
}
|
|
411
|
-
|
|
412
|
-
// Only the joined subqueries select aggregates by their aliases. The root
|
|
413
|
-
// query keeps treating the columns passed to them as selected.
|
|
414
|
-
function findUserSelections(builder, tableNode) {
|
|
415
|
-
if (tableNode.parentNode) {
|
|
416
|
-
return findJoinSelections(builder);
|
|
417
|
-
} else {
|
|
418
|
-
return builder.findAllSelections();
|
|
419
|
-
}
|
|
420
|
-
}
|
|
421
|
-
|
|
422
401
|
function createHasUserSelectionAs(builder, tableNode) {
|
|
423
402
|
if (tableNode.parentNode) {
|
|
424
|
-
return
|
|
403
|
+
return (column) => builder.hasSelectionAs(column, column);
|
|
425
404
|
} else {
|
|
426
405
|
// Qualified, as the root query can also select the columns of the joined tables.
|
|
427
406
|
return (column) => builder.hasSelectionAs(`${tableNode.alias}.${column}`, column);
|
|
428
407
|
}
|
|
429
408
|
}
|
|
430
409
|
|
|
431
|
-
function isAggregateOperation(op) {
|
|
432
|
-
return op.constructor === SelectOperation && op.aggregateSelections !== null;
|
|
433
|
-
}
|
|
434
|
-
|
|
435
410
|
function mapUserSelectionsFromSubqueryToMainQuery({ userSelections, tableNode }) {
|
|
436
411
|
return userSelections.filter(isNotSelectAll).map((selection) => {
|
|
437
412
|
return new Selection(
|
|
@@ -490,7 +465,7 @@ function removeRootSelectAll(builder) {
|
|
|
490
465
|
selection.column === '*' &&
|
|
491
466
|
(!selection.table || isSameTable(builder, selection.table, tableRef));
|
|
492
467
|
const isSelect = (op) =>
|
|
493
|
-
op.constructor === SelectOperation && !
|
|
468
|
+
op.constructor === SelectOperation && !op.isAggregate && !op.isDistinctOn;
|
|
494
469
|
|
|
495
470
|
builder.forEachOperation(isSelect, (op) => {
|
|
496
471
|
op.args = op.args.filter((arg) => {
|
|
@@ -524,17 +499,28 @@ function isNotSelectAll(selection) {
|
|
|
524
499
|
return selection.column !== '*';
|
|
525
500
|
}
|
|
526
501
|
|
|
527
|
-
|
|
528
|
-
|
|
529
|
-
|
|
502
|
+
// Aggregates without an alias select no column that we can name, but they
|
|
503
|
+
// still replace the select all.
|
|
504
|
+
function isSelectAllSelectionSet(builder) {
|
|
505
|
+
const selections = builder.findAllSelections().filter(isNotKeepingSelectAll);
|
|
506
|
+
return (selections.length === 0 && !builder.has(isAggregate)) || selections.some(isSelectAll);
|
|
530
507
|
}
|
|
531
508
|
|
|
532
509
|
function isNotKeepingSelectAll(selection) {
|
|
533
510
|
return !selection.keepsSelectAll;
|
|
534
511
|
}
|
|
535
512
|
|
|
536
|
-
function selectsOnlyAliasesKeepingSelectAll(
|
|
537
|
-
|
|
513
|
+
function selectsOnlyAliasesKeepingSelectAll(builder) {
|
|
514
|
+
const selections = builder.findAllSelections();
|
|
515
|
+
return (
|
|
516
|
+
selections.length > 0 &&
|
|
517
|
+
selections.every((it) => it.keepsSelectAll) &&
|
|
518
|
+
!builder.has(isAggregate)
|
|
519
|
+
);
|
|
520
|
+
}
|
|
521
|
+
|
|
522
|
+
function isAggregate(op) {
|
|
523
|
+
return op.constructor === SelectOperation && op.isAggregate;
|
|
538
524
|
}
|
|
539
525
|
|
|
540
526
|
function checkAliasLength(modelClass, alias, lengthLimit) {
|
|
@@ -547,7 +533,3 @@ function checkAliasLength(modelClass, alias, lengthLimit) {
|
|
|
547
533
|
});
|
|
548
534
|
}
|
|
549
535
|
}
|
|
550
|
-
|
|
551
|
-
module.exports = {
|
|
552
|
-
RelationJoiner,
|
|
553
|
-
};
|
|
@@ -1,8 +1,6 @@
|
|
|
1
|
-
|
|
1
|
+
import { propKey, hasProps } from '../../model/modelValues.js';
|
|
2
2
|
|
|
3
|
-
|
|
4
|
-
|
|
5
|
-
class TableNode {
|
|
3
|
+
export class TableNode {
|
|
6
4
|
constructor({ tableTree, modelClass, expression, parentNode = null, relation = null }) {
|
|
7
5
|
this.tableTree = tableTree;
|
|
8
6
|
this.modelClass = modelClass;
|
|
@@ -95,7 +93,3 @@ class TableNode {
|
|
|
95
93
|
return (flatRow) => (hasProps(flatRow, columnAliases) ? propKey(flatRow, columnAliases) : null);
|
|
96
94
|
}
|
|
97
95
|
}
|
|
98
|
-
|
|
99
|
-
module.exports = {
|
|
100
|
-
TableNode,
|
|
101
|
-
};
|
|
@@ -1,9 +1,7 @@
|
|
|
1
|
-
|
|
1
|
+
import { forEachChildExpression } from './utils.js';
|
|
2
|
+
import { TableNode } from './TableNode.js';
|
|
2
3
|
|
|
3
|
-
|
|
4
|
-
const { TableNode } = require('./TableNode');
|
|
5
|
-
|
|
6
|
-
class TableTree {
|
|
4
|
+
export class TableTree {
|
|
7
5
|
constructor({ expression, rootModelClass, rootTableAlias, options }) {
|
|
8
6
|
this.options = options;
|
|
9
7
|
this.rootModelClass = rootModelClass;
|
|
@@ -79,7 +77,3 @@ class TableTree {
|
|
|
79
77
|
});
|
|
80
78
|
}
|
|
81
79
|
}
|
|
82
|
-
|
|
83
|
-
module.exports = {
|
|
84
|
-
TableTree,
|
|
85
|
-
};
|
|
@@ -1,14 +1,12 @@
|
|
|
1
|
-
|
|
1
|
+
import { ValidationErrorType } from '../../model/ValidationError.js';
|
|
2
|
+
import { isPostgres, isMySql, isMsSql, isSqlite } from '../../utils/knexUtils.js';
|
|
2
3
|
|
|
3
|
-
const
|
|
4
|
-
const { isPostgres, isMySql, isMsSql, isSqlite } = require('../../utils/knexUtils');
|
|
5
|
-
|
|
6
|
-
const RELATION_RECURSION_LIMIT = 64;
|
|
4
|
+
export const RELATION_RECURSION_LIMIT = 64;
|
|
7
5
|
|
|
8
6
|
// The maximum length of the column aliases of the joined relations, after
|
|
9
7
|
// which the database would truncate them. Other databases keep the limit of
|
|
10
8
|
// postgres, which isn't the strictest one: Oracle before 12.2 only allows 30.
|
|
11
|
-
function getIdentifierLengthLimit(knex) {
|
|
9
|
+
export function getIdentifierLengthLimit(knex) {
|
|
12
10
|
if (isPostgres(knex)) {
|
|
13
11
|
return 63;
|
|
14
12
|
} else if (isMySql(knex)) {
|
|
@@ -23,7 +21,7 @@ function getIdentifierLengthLimit(knex) {
|
|
|
23
21
|
}
|
|
24
22
|
|
|
25
23
|
// Given a relation expression, goes through all first level children.
|
|
26
|
-
function forEachChildExpression(expr, modelClass, callback) {
|
|
24
|
+
export function forEachChildExpression(expr, modelClass, callback) {
|
|
27
25
|
if (expr.node.$allRecursive || expr.maxRecursionDepth > RELATION_RECURSION_LIMIT) {
|
|
28
26
|
throw modelClass.createValidationError({
|
|
29
27
|
type: ValidationErrorType.RelationExpression,
|
|
@@ -33,11 +31,3 @@ function forEachChildExpression(expr, modelClass, callback) {
|
|
|
33
31
|
|
|
34
32
|
expr.forEachChildExpression(modelClass, callback);
|
|
35
33
|
}
|
|
36
|
-
|
|
37
|
-
module.exports = {
|
|
38
|
-
RELATION_RECURSION_LIMIT,
|
|
39
|
-
|
|
40
|
-
getIdentifierLengthLimit,
|
|
41
|
-
|
|
42
|
-
forEachChildExpression,
|
|
43
|
-
};
|
|
@@ -0,0 +1,51 @@
|
|
|
1
|
+
// Renders the SQL of json field expressions like `ref('column:a.b')`, their
|
|
2
|
+
// casts and the json values of `val()` for one database. The json methods are
|
|
3
|
+
// implemented by the subclasses of the databases that support them.
|
|
4
|
+
export class JsonDialect {
|
|
5
|
+
// Throw or warn for databases that can't render json field expressions.
|
|
6
|
+
// @overridable
|
|
7
|
+
checkSupport() {}
|
|
8
|
+
|
|
9
|
+
// @overridable
|
|
10
|
+
cast(sql, type) {
|
|
11
|
+
return `CAST(${sql} AS ${type})`;
|
|
12
|
+
}
|
|
13
|
+
|
|
14
|
+
// Turn the json text of a binding into a json value.
|
|
15
|
+
// @overridable
|
|
16
|
+
json(sql) {
|
|
17
|
+
return sql;
|
|
18
|
+
}
|
|
19
|
+
|
|
20
|
+
// Reference the bare column if there is no path, so that the database can use
|
|
21
|
+
// its indexes.
|
|
22
|
+
field(sql, access) {
|
|
23
|
+
return access.length > 0 ? this.extract(sql, access) : sql;
|
|
24
|
+
}
|
|
25
|
+
|
|
26
|
+
// Turn the operands of `superset()` and `subset()` into json values.
|
|
27
|
+
// @overridable
|
|
28
|
+
jsonOperand(sql) {
|
|
29
|
+
return sql;
|
|
30
|
+
}
|
|
31
|
+
|
|
32
|
+
// The operands are pairs of SQL and bindings, so they can swap places:
|
|
33
|
+
// @overridable
|
|
34
|
+
subset(left, right) {
|
|
35
|
+
return this.superset(right, left);
|
|
36
|
+
}
|
|
37
|
+
|
|
38
|
+
// Return the SQL and bindings, as the column may appear more than once.
|
|
39
|
+
// @overridable
|
|
40
|
+
hasKeys(column, access, keys, all) {
|
|
41
|
+
if (all) {
|
|
42
|
+
const parts = keys.map((key) => this.hasAnyKey(column, access, [key]));
|
|
43
|
+
return [
|
|
44
|
+
`(${parts.map(([sql]) => sql).join(' and ')})`,
|
|
45
|
+
parts.flatMap(([, bindings]) => bindings),
|
|
46
|
+
];
|
|
47
|
+
} else {
|
|
48
|
+
return this.hasAnyKey(column, access, keys);
|
|
49
|
+
}
|
|
50
|
+
}
|
|
51
|
+
}
|
|
@@ -0,0 +1,81 @@
|
|
|
1
|
+
import { JsonDialect } from './JsonDialect.js';
|
|
2
|
+
import { toJsonPath } from '../../utils/parseFieldExpression.js';
|
|
3
|
+
|
|
4
|
+
// MySQL can only cast to these types, see `CAST()` in its manual. `float` is a
|
|
5
|
+
// double precision number in Postgres, `real` a single precision one, and the
|
|
6
|
+
// precision of `decimal` is the maximum, as MySQL's default rounds to integers.
|
|
7
|
+
const castTypes = {
|
|
8
|
+
text: 'CHAR',
|
|
9
|
+
integer: 'SIGNED',
|
|
10
|
+
bigint: 'SIGNED',
|
|
11
|
+
boolean: 'SIGNED',
|
|
12
|
+
float: 'DOUBLE',
|
|
13
|
+
real: 'FLOAT',
|
|
14
|
+
decimal: 'DECIMAL(65,30)',
|
|
15
|
+
jsonb: 'JSON',
|
|
16
|
+
};
|
|
17
|
+
|
|
18
|
+
export class MySqlJsonDialect extends JsonDialect {
|
|
19
|
+
// Casting json values works for all types but text, where json strings need
|
|
20
|
+
// to be unquoted. Json null is cast to 0 or 'null', so turn it into NULL like
|
|
21
|
+
// Postgres and SQLite do.
|
|
22
|
+
extract(sql, access, cast) {
|
|
23
|
+
const value = `JSON_EXTRACT(${sql}, ${toMySqlJsonPathLiteral(access)})`;
|
|
24
|
+
if (cast) {
|
|
25
|
+
const nullable = `NULLIF(${value}, CAST('null' AS JSON))`;
|
|
26
|
+
return /^char\b/i.test(castTypes[cast] || cast) ? `JSON_UNQUOTE(${nullable})` : nullable;
|
|
27
|
+
} else {
|
|
28
|
+
return value;
|
|
29
|
+
}
|
|
30
|
+
}
|
|
31
|
+
|
|
32
|
+
// Like `to_jsonb()`, turn SQL values into json and keep json values as they are:
|
|
33
|
+
toJson(sql) {
|
|
34
|
+
return `JSON_EXTRACT(JSON_ARRAY(${sql}), '$[0]')`;
|
|
35
|
+
}
|
|
36
|
+
|
|
37
|
+
set(sql, access, value) {
|
|
38
|
+
return `JSON_SET(${sql}, ${toMySqlJsonPathLiteral(access)}, ${value})`;
|
|
39
|
+
}
|
|
40
|
+
|
|
41
|
+
superset([left, leftBindings], [right, rightBindings]) {
|
|
42
|
+
return [`JSON_CONTAINS(${left}, ${right})`, [...leftBindings, ...rightBindings]];
|
|
43
|
+
}
|
|
44
|
+
|
|
45
|
+
isType(sql, access, type) {
|
|
46
|
+
return `JSON_TYPE(${this.field(sql, access)}) = '${type.toUpperCase()}'`;
|
|
47
|
+
}
|
|
48
|
+
|
|
49
|
+
// Like the `?|` operator of Postgres, find the keys of objects, the strings in
|
|
50
|
+
// arrays and strings:
|
|
51
|
+
hasAnyKey(column, access, keys) {
|
|
52
|
+
const value = this.field('??', access);
|
|
53
|
+
return [
|
|
54
|
+
`JSON_OVERLAPS(COALESCE(JSON_KEYS(${value}), ${value}), ` +
|
|
55
|
+
`JSON_ARRAY(${keys.map(() => '?').join(', ')}))`,
|
|
56
|
+
[column, column, ...keys],
|
|
57
|
+
];
|
|
58
|
+
}
|
|
59
|
+
|
|
60
|
+
// @override
|
|
61
|
+
cast(sql, type) {
|
|
62
|
+
return super.cast(sql, castTypes[type] || type);
|
|
63
|
+
}
|
|
64
|
+
|
|
65
|
+
// @override
|
|
66
|
+
json(sql) {
|
|
67
|
+
return `CAST(${sql} AS JSON)`;
|
|
68
|
+
}
|
|
69
|
+
}
|
|
70
|
+
|
|
71
|
+
// Formats the json path of a parsed field expression as a string literal, e.g.
|
|
72
|
+
// `'$."a"[0]'`. Like in `toPostgresJsonPathLiteral()`, the path can't be a
|
|
73
|
+
// binding. Paths with `?` or backslashes are written as hex literals: the mysql
|
|
74
|
+
// driver reads every `?` as a binding placeholder, and whether backslashes
|
|
75
|
+
// escape in string literals depends on the `NO_BACKSLASH_ESCAPES` SQL mode.
|
|
76
|
+
function toMySqlJsonPathLiteral(access) {
|
|
77
|
+
const path = toJsonPath(access);
|
|
78
|
+
return /[?\\]/.test(path)
|
|
79
|
+
? `_utf8mb4 X'${Buffer.from(path).toString('hex')}'`
|
|
80
|
+
: `'${path.replace(/'/g, "''")}'`;
|
|
81
|
+
}
|
|
@@ -0,0 +1,45 @@
|
|
|
1
|
+
import { JsonDialect } from './JsonDialect.js';
|
|
2
|
+
import { toPostgresJsonPathLiteral } from '../../utils/parseFieldExpression.js';
|
|
3
|
+
|
|
4
|
+
export class PostgresJsonDialect extends JsonDialect {
|
|
5
|
+
// Casts need the value as text, e.g. `CAST('"a"'::jsonb AS text)` would keep
|
|
6
|
+
// the quotes.
|
|
7
|
+
extract(sql, access, cast) {
|
|
8
|
+
return `${sql}${cast ? '#>>' : '#>'}${toPostgresJsonPathLiteral(access)}`;
|
|
9
|
+
}
|
|
10
|
+
|
|
11
|
+
toJson(sql) {
|
|
12
|
+
return `to_jsonb(${sql})`;
|
|
13
|
+
}
|
|
14
|
+
|
|
15
|
+
set(sql, access, value) {
|
|
16
|
+
return `jsonb_set(${sql}, ${toPostgresJsonPathLiteral(access)}, ${value}, true)`;
|
|
17
|
+
}
|
|
18
|
+
|
|
19
|
+
// The json operators need jsonb, also for json columns:
|
|
20
|
+
// @override
|
|
21
|
+
jsonOperand(sql) {
|
|
22
|
+
return `( ${sql} )::jsonb`;
|
|
23
|
+
}
|
|
24
|
+
|
|
25
|
+
superset([left, leftBindings], [right, rightBindings]) {
|
|
26
|
+
return [`${left} @> ${right}`, [...leftBindings, ...rightBindings]];
|
|
27
|
+
}
|
|
28
|
+
|
|
29
|
+
// @override
|
|
30
|
+
subset([left, leftBindings], [right, rightBindings]) {
|
|
31
|
+
return [`${left} <@ ${right}`, [...leftBindings, ...rightBindings]];
|
|
32
|
+
}
|
|
33
|
+
|
|
34
|
+
isType(sql, access, type) {
|
|
35
|
+
return `jsonb_typeof(${this.jsonOperand(this.field(sql, access))}) = '${type}'`;
|
|
36
|
+
}
|
|
37
|
+
|
|
38
|
+
// @override
|
|
39
|
+
hasKeys(column, access, keys, all) {
|
|
40
|
+
return [
|
|
41
|
+
`${this.field('??', access)} ${all ? '\\?&' : '\\?|'} array[${keys.map(() => '?').join(',')}]`,
|
|
42
|
+
[column, ...keys],
|
|
43
|
+
];
|
|
44
|
+
}
|
|
45
|
+
}
|
|
@@ -0,0 +1,67 @@
|
|
|
1
|
+
import { JsonDialect } from './JsonDialect.js';
|
|
2
|
+
import { toJsonPath } from '../../utils/parseFieldExpression.js';
|
|
3
|
+
|
|
4
|
+
export class SqliteJsonDialect extends JsonDialect {
|
|
5
|
+
// `json_extract()` returns SQL values, which compare and sort like json
|
|
6
|
+
// values, unlike the json text that `->` returns.
|
|
7
|
+
extract(sql, access) {
|
|
8
|
+
return `json_extract(${sql}, ${toSqliteJsonPathLiteral(access)})`;
|
|
9
|
+
}
|
|
10
|
+
|
|
11
|
+
// `json_quote()` keeps the values of json functions as json, but SQLite can't
|
|
12
|
+
// tell json columns from text columns, which it would quote:
|
|
13
|
+
toJson(sql, isColumn) {
|
|
14
|
+
if (isColumn) {
|
|
15
|
+
throw new Error(
|
|
16
|
+
"ref().castJson() needs a json path or a cast on SQLite, which can't tell json columns " +
|
|
17
|
+
'from text columns.',
|
|
18
|
+
);
|
|
19
|
+
}
|
|
20
|
+
return `json_quote(${sql})`;
|
|
21
|
+
}
|
|
22
|
+
|
|
23
|
+
set(sql, access, value) {
|
|
24
|
+
return `json_set(${sql}, ${toSqliteJsonPathLiteral(access)}, ${value})`;
|
|
25
|
+
}
|
|
26
|
+
|
|
27
|
+
superset() {
|
|
28
|
+
throw new Error(
|
|
29
|
+
'whereJsonSupersetOf() and whereJsonSubsetOf() are not supported on SQLite, which has no ' +
|
|
30
|
+
'json containment.',
|
|
31
|
+
);
|
|
32
|
+
}
|
|
33
|
+
|
|
34
|
+
isType(sql, access, type) {
|
|
35
|
+
return `json_type(${sql}, ${toSqliteJsonPathLiteral(access)}) = '${type}'`;
|
|
36
|
+
}
|
|
37
|
+
|
|
38
|
+
// Like the `?|` operator of Postgres, find the keys of objects, the strings in
|
|
39
|
+
// arrays and strings. The keys of objects are text, those of arrays integers:
|
|
40
|
+
hasAnyKey(column, access, keys) {
|
|
41
|
+
return [
|
|
42
|
+
`exists (select 1 from json_each(??, ${toSqliteJsonPathLiteral(access)}) ` +
|
|
43
|
+
"where case when typeof(json_each.key) = 'text' then json_each.key " +
|
|
44
|
+
`when json_each.type = 'text' then json_each.value end in (${keys.map(() => '?').join(', ')}))`,
|
|
45
|
+
[column, ...keys],
|
|
46
|
+
];
|
|
47
|
+
}
|
|
48
|
+
|
|
49
|
+
// SQLite accepts any type name, but not as json:
|
|
50
|
+
// @override
|
|
51
|
+
cast(sql, type) {
|
|
52
|
+
return type === 'jsonb' ? this.json(sql) : super.cast(sql, type);
|
|
53
|
+
}
|
|
54
|
+
|
|
55
|
+
// @override
|
|
56
|
+
json(sql) {
|
|
57
|
+
return `json(${sql})`;
|
|
58
|
+
}
|
|
59
|
+
}
|
|
60
|
+
|
|
61
|
+
// Formats the json path of a parsed field expression as a string literal, e.g.
|
|
62
|
+
// `'$."a"[0]'`. Like in `toPostgresJsonPathLiteral()`, the path can't be a
|
|
63
|
+
// binding, and knex reads every `?` in it as a binding placeholder. SQLite
|
|
64
|
+
// understands json escapes in quoted keys, so `?` is written as `\u003f`.
|
|
65
|
+
function toSqliteJsonPathLiteral(access) {
|
|
66
|
+
return `'${toJsonPath(access).replace(/\?/g, '\\u003f').replace(/'/g, "''")}'`;
|
|
67
|
+
}
|
|
@@ -0,0 +1,44 @@
|
|
|
1
|
+
import { JsonDialect } from './JsonDialect.js';
|
|
2
|
+
import { PostgresJsonDialect } from './PostgresJsonDialect.js';
|
|
3
|
+
import { MySqlJsonDialect } from './MySqlJsonDialect.js';
|
|
4
|
+
import { SqliteJsonDialect } from './SqliteJsonDialect.js';
|
|
5
|
+
import { isPostgres, isMySql, isSqlite, isMsSql, isOracle } from '../../utils/knexUtils.js';
|
|
6
|
+
import { warnOnce } from '../../utils/warnings.js';
|
|
7
|
+
|
|
8
|
+
const message =
|
|
9
|
+
'JSON field expressions and the whereJson*() methods of objection are only supported on ' +
|
|
10
|
+
'PostgreSQL, MySQL and SQLite. Use the JSON methods of knex like whereJsonPath() or ' +
|
|
11
|
+
'jsonExtract() with other databases.';
|
|
12
|
+
|
|
13
|
+
class UnsupportedJsonDialect extends JsonDialect {
|
|
14
|
+
// @override
|
|
15
|
+
checkSupport() {
|
|
16
|
+
throw new Error(message);
|
|
17
|
+
}
|
|
18
|
+
}
|
|
19
|
+
|
|
20
|
+
// Other databases may understand the SQL of Postgres, so only warn:
|
|
21
|
+
class UnknownJsonDialect extends PostgresJsonDialect {
|
|
22
|
+
// @override
|
|
23
|
+
checkSupport() {
|
|
24
|
+
warnOnce(message);
|
|
25
|
+
}
|
|
26
|
+
}
|
|
27
|
+
|
|
28
|
+
const postgres = new PostgresJsonDialect();
|
|
29
|
+
const mySql = new MySqlJsonDialect();
|
|
30
|
+
const sqlite = new SqliteJsonDialect();
|
|
31
|
+
const unsupported = new UnsupportedJsonDialect();
|
|
32
|
+
const unknown = new UnknownJsonDialect();
|
|
33
|
+
|
|
34
|
+
export function getJsonDialect(knex) {
|
|
35
|
+
return isPostgres(knex)
|
|
36
|
+
? postgres
|
|
37
|
+
: isMySql(knex)
|
|
38
|
+
? mySql
|
|
39
|
+
: isSqlite(knex)
|
|
40
|
+
? sqlite
|
|
41
|
+
: isMsSql(knex) || isOracle(knex)
|
|
42
|
+
? unsupported
|
|
43
|
+
: unknown;
|
|
44
|
+
}
|