@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,164 @@
|
|
|
1
|
+
'use strict';
|
|
2
|
+
|
|
3
|
+
const parser = require('../../../utils/parseFieldExpression');
|
|
4
|
+
const { asArray, isObject, isString } = require('../../../utils/objectUtils');
|
|
5
|
+
const { toPgJsonPathLiteral, toPgQuotedColumn } = require('../../../utils/postgresJsonPath');
|
|
6
|
+
|
|
7
|
+
/**
|
|
8
|
+
* @typedef {String} FieldExpression
|
|
9
|
+
*
|
|
10
|
+
* Field expressions allow one to refer to separate JSONB fields inside columns.
|
|
11
|
+
*
|
|
12
|
+
* Syntax: <column reference>[:<json field reference>]
|
|
13
|
+
*
|
|
14
|
+
* e.g. `Person.jsonColumnName:details.names[1]` would refer to value `'Second'`
|
|
15
|
+
* in column `Person.jsonColumnName` which has
|
|
16
|
+
* `{ details: { names: ['First', 'Second', 'Last'] } }` object stored in it.
|
|
17
|
+
*
|
|
18
|
+
* First part `<column reference>` is compatible with column references used in
|
|
19
|
+
* knex e.g. `MyFancyTable.tributeToThBestColumnNameEver`.
|
|
20
|
+
*
|
|
21
|
+
* Second part describes a path to an attribute inside the referred column.
|
|
22
|
+
* It is optional and it always starts with colon which follows directly with
|
|
23
|
+
* first path element. e.g. `Table.jsonObjectColumnName:jsonFieldName` or
|
|
24
|
+
* `Table.jsonArrayColumn:[321]`.
|
|
25
|
+
*
|
|
26
|
+
* Syntax supports `[<key or index>]` and `.<key or index>` flavors of reference
|
|
27
|
+
* to json keys / array indexes:
|
|
28
|
+
*
|
|
29
|
+
* e.g. both `Table.myColumn:[1][3]` and `Table.myColumn:1.3` would access correctly
|
|
30
|
+
* both of the following objects `[null, [null,null,null, "I was accessed"]]` and
|
|
31
|
+
* `{ "1": { "3" : "I was accessed" } }`
|
|
32
|
+
*
|
|
33
|
+
* Caveats when using special characters in keys:
|
|
34
|
+
*
|
|
35
|
+
* 1. `objectColumn.key` This is the most common syntax, good if you are
|
|
36
|
+
* not using dots or square brackets `[]` in your json object key name.
|
|
37
|
+
* 2. Keys containing dots `objectColumn:[keywith.dots]` Column `{ "keywith.dots" : "I was referred" }`
|
|
38
|
+
* 3. Keys containing square brackets `column['[]']` `{ "[]" : "This is getting ridiculous..." }`
|
|
39
|
+
* 4. Keys containing square brackets and quotes
|
|
40
|
+
* `objectColumn:['Double."Quote".[]']` and `objectColumn:["Sinlge.'Quote'.[]"]`
|
|
41
|
+
* Column `{ "Double.\"Quote\".[]" : "I was referred", "Single.'Quote'.[]" : "Mee too!" }`
|
|
42
|
+
* 99. Keys containing dots, square brackets, single quotes and double quotes in one json key is
|
|
43
|
+
* not currently supported
|
|
44
|
+
*/
|
|
45
|
+
|
|
46
|
+
function parseFieldExpression(expression, extractAsText) {
|
|
47
|
+
let parsed = parser.parseFieldExpression(expression);
|
|
48
|
+
let extractor = extractAsText ? '#>>' : '#>';
|
|
49
|
+
return `${toPgQuotedColumn(parsed.columnName)}${extractor}${toPgJsonPathLiteral(parsed.access)}`;
|
|
50
|
+
}
|
|
51
|
+
|
|
52
|
+
function whereJsonbRefOnLeftJsonbValOrRefOnRight(
|
|
53
|
+
builder,
|
|
54
|
+
fieldExpression,
|
|
55
|
+
operator,
|
|
56
|
+
jsonObjectOrFieldExpression,
|
|
57
|
+
queryPrefix,
|
|
58
|
+
) {
|
|
59
|
+
let queryParams = whereJsonbRefOnLeftJsonbValOrRefOnRightRawQueryParams(
|
|
60
|
+
fieldExpression,
|
|
61
|
+
operator,
|
|
62
|
+
jsonObjectOrFieldExpression,
|
|
63
|
+
queryPrefix,
|
|
64
|
+
);
|
|
65
|
+
return builder.whereRaw.apply(builder, queryParams);
|
|
66
|
+
}
|
|
67
|
+
|
|
68
|
+
function whereJsonbRefOnLeftJsonbValOrRefOnRightRawQueryParams(
|
|
69
|
+
fieldExpression,
|
|
70
|
+
operator,
|
|
71
|
+
jsonObjectOrFieldExpression,
|
|
72
|
+
queryPrefix,
|
|
73
|
+
) {
|
|
74
|
+
let fieldReference = parseFieldExpression(fieldExpression);
|
|
75
|
+
|
|
76
|
+
if (isString(jsonObjectOrFieldExpression)) {
|
|
77
|
+
let rightHandReference = parseFieldExpression(jsonObjectOrFieldExpression);
|
|
78
|
+
let refRefQuery = [
|
|
79
|
+
'(',
|
|
80
|
+
fieldReference,
|
|
81
|
+
')::jsonb',
|
|
82
|
+
operator,
|
|
83
|
+
'(',
|
|
84
|
+
rightHandReference,
|
|
85
|
+
')::jsonb',
|
|
86
|
+
];
|
|
87
|
+
if (queryPrefix) {
|
|
88
|
+
refRefQuery.unshift(queryPrefix);
|
|
89
|
+
}
|
|
90
|
+
return [refRefQuery.join(' ')];
|
|
91
|
+
} else if (isObject(jsonObjectOrFieldExpression)) {
|
|
92
|
+
let refValQuery = ['(', fieldReference, ')::jsonb', operator, '?::jsonb'];
|
|
93
|
+
if (queryPrefix) {
|
|
94
|
+
refValQuery.unshift(queryPrefix);
|
|
95
|
+
}
|
|
96
|
+
return [refValQuery.join(' '), JSON.stringify(jsonObjectOrFieldExpression)];
|
|
97
|
+
}
|
|
98
|
+
|
|
99
|
+
throw new Error('Invalid right hand expression.');
|
|
100
|
+
}
|
|
101
|
+
|
|
102
|
+
function whereJsonFieldRightStringArrayOnLeftQuery(knex, fieldExpression, operator, keys) {
|
|
103
|
+
let fieldReference = parseFieldExpression(fieldExpression);
|
|
104
|
+
keys = asArray(keys);
|
|
105
|
+
|
|
106
|
+
let questionMarksArray = keys.map((key) => {
|
|
107
|
+
if (!isString(key)) {
|
|
108
|
+
throw new Error('All keys to find must be strings.');
|
|
109
|
+
}
|
|
110
|
+
return '?';
|
|
111
|
+
});
|
|
112
|
+
|
|
113
|
+
let rightHandExpression = 'array[' + questionMarksArray.join(',') + ']';
|
|
114
|
+
|
|
115
|
+
return [`${fieldReference} ${operator.replace('?', '\\?')} ${rightHandExpression}`, keys];
|
|
116
|
+
}
|
|
117
|
+
|
|
118
|
+
function whereJsonFieldQuery(knex, fieldExpression, operator, value) {
|
|
119
|
+
let fieldReference = parseFieldExpression(fieldExpression, true);
|
|
120
|
+
let normalizedOperator = normalizeOperator(knex, operator);
|
|
121
|
+
|
|
122
|
+
// json type comparison takes json type in string format
|
|
123
|
+
let cast;
|
|
124
|
+
let valuePlaceholder = '?';
|
|
125
|
+
let bindings = [value];
|
|
126
|
+
let type = typeof value;
|
|
127
|
+
|
|
128
|
+
if (type === 'number') {
|
|
129
|
+
cast = '::NUMERIC';
|
|
130
|
+
} else if (type === 'boolean') {
|
|
131
|
+
cast = '::BOOLEAN';
|
|
132
|
+
} else if (type === 'string') {
|
|
133
|
+
cast = '::TEXT';
|
|
134
|
+
} else if (value === null) {
|
|
135
|
+
cast = '::TEXT';
|
|
136
|
+
valuePlaceholder = 'NULL';
|
|
137
|
+
bindings = [];
|
|
138
|
+
} else {
|
|
139
|
+
throw new Error('Value must be string, number, boolean or null.');
|
|
140
|
+
}
|
|
141
|
+
|
|
142
|
+
return [`(${fieldReference})${cast} ${normalizedOperator} ${valuePlaceholder}`, bindings];
|
|
143
|
+
}
|
|
144
|
+
|
|
145
|
+
function normalizeOperator(knex, operator) {
|
|
146
|
+
let trimmedLowerCase = operator.trim().toLowerCase();
|
|
147
|
+
|
|
148
|
+
switch (trimmedLowerCase) {
|
|
149
|
+
case 'is':
|
|
150
|
+
case 'is not':
|
|
151
|
+
return trimmedLowerCase;
|
|
152
|
+
default:
|
|
153
|
+
return knex.client.formatter().operator(operator);
|
|
154
|
+
}
|
|
155
|
+
}
|
|
156
|
+
|
|
157
|
+
module.exports = {
|
|
158
|
+
parseFieldExpression: parseFieldExpression,
|
|
159
|
+
whereJsonbRefOnLeftJsonbValOrRefOnRight: whereJsonbRefOnLeftJsonbValOrRefOnRight,
|
|
160
|
+
whereJsonbRefOnLeftJsonbValOrRefOnRightRawQueryParams:
|
|
161
|
+
whereJsonbRefOnLeftJsonbValOrRefOnRightRawQueryParams,
|
|
162
|
+
whereJsonFieldRightStringArrayOnLeftQuery: whereJsonFieldRightStringArrayOnLeftQuery,
|
|
163
|
+
whereJsonFieldQuery: whereJsonFieldQuery,
|
|
164
|
+
};
|
|
@@ -0,0 +1,66 @@
|
|
|
1
|
+
'use strict';
|
|
2
|
+
|
|
3
|
+
const { flatten } = require('../../../utils/objectUtils');
|
|
4
|
+
const { Selection } = require('./Selection');
|
|
5
|
+
const { ObjectionToKnexConvertingOperation } = require('../ObjectionToKnexConvertingOperation');
|
|
6
|
+
|
|
7
|
+
const COUNT_REGEX = /count/i;
|
|
8
|
+
|
|
9
|
+
class SelectOperation extends ObjectionToKnexConvertingOperation {
|
|
10
|
+
constructor(name, opt) {
|
|
11
|
+
super(name, opt);
|
|
12
|
+
this.selections = [];
|
|
13
|
+
}
|
|
14
|
+
|
|
15
|
+
onAdd(builder, args) {
|
|
16
|
+
const selections = flatten(args);
|
|
17
|
+
|
|
18
|
+
// Don't add an empty selection. Empty list is accepted for `count`, `countDistinct`
|
|
19
|
+
// etc. because knex apparently supports it.
|
|
20
|
+
if (selections.length === 0 && !COUNT_REGEX.test(this.name)) {
|
|
21
|
+
return false;
|
|
22
|
+
}
|
|
23
|
+
|
|
24
|
+
const ret = super.onAdd(builder, selections);
|
|
25
|
+
|
|
26
|
+
for (const selection of selections) {
|
|
27
|
+
const selectionInstance = Selection.create(selection);
|
|
28
|
+
|
|
29
|
+
if (selectionInstance) {
|
|
30
|
+
this.selections.push(selectionInstance);
|
|
31
|
+
}
|
|
32
|
+
}
|
|
33
|
+
|
|
34
|
+
return ret;
|
|
35
|
+
}
|
|
36
|
+
|
|
37
|
+
onBuildKnex(knexBuilder, builder) {
|
|
38
|
+
return knexBuilder[this.name].apply(knexBuilder, this.getKnexArgs(builder));
|
|
39
|
+
}
|
|
40
|
+
|
|
41
|
+
findSelection(builder, selectionToFind) {
|
|
42
|
+
const selectionInstanceToFind = Selection.create(selectionToFind);
|
|
43
|
+
|
|
44
|
+
if (!selectionInstanceToFind) {
|
|
45
|
+
return null;
|
|
46
|
+
}
|
|
47
|
+
|
|
48
|
+
for (const selection of this.selections) {
|
|
49
|
+
if (Selection.doesSelect(builder, selection, selectionInstanceToFind)) {
|
|
50
|
+
return selection;
|
|
51
|
+
}
|
|
52
|
+
}
|
|
53
|
+
|
|
54
|
+
return null;
|
|
55
|
+
}
|
|
56
|
+
|
|
57
|
+
clone() {
|
|
58
|
+
const clone = super.clone();
|
|
59
|
+
clone.selections = this.selections.slice();
|
|
60
|
+
return clone;
|
|
61
|
+
}
|
|
62
|
+
}
|
|
63
|
+
|
|
64
|
+
module.exports = {
|
|
65
|
+
SelectOperation,
|
|
66
|
+
};
|
|
@@ -0,0 +1,123 @@
|
|
|
1
|
+
'use strict';
|
|
2
|
+
|
|
3
|
+
const { isString, isObject } = require('../../../utils/objectUtils');
|
|
4
|
+
|
|
5
|
+
const ALIAS_REGEX = /\s+as\s+/i;
|
|
6
|
+
|
|
7
|
+
class Selection {
|
|
8
|
+
constructor(table, column, alias) {
|
|
9
|
+
this.table = table;
|
|
10
|
+
this.column = column;
|
|
11
|
+
this.alias = alias;
|
|
12
|
+
}
|
|
13
|
+
|
|
14
|
+
get name() {
|
|
15
|
+
return this.alias || this.column;
|
|
16
|
+
}
|
|
17
|
+
|
|
18
|
+
static create(selection) {
|
|
19
|
+
if (isObject(selection)) {
|
|
20
|
+
if (selection.isObjectionSelection) {
|
|
21
|
+
return selection;
|
|
22
|
+
} else if (selection.isObjectionReferenceBuilder) {
|
|
23
|
+
return createSelectionFromReference(selection);
|
|
24
|
+
} else if (selection.isObjectionRawBuilder) {
|
|
25
|
+
return createSelectionFromRaw(selection);
|
|
26
|
+
} else {
|
|
27
|
+
return null;
|
|
28
|
+
}
|
|
29
|
+
} else if (isString(selection)) {
|
|
30
|
+
return createSelectionFromString(selection);
|
|
31
|
+
} else {
|
|
32
|
+
return null;
|
|
33
|
+
}
|
|
34
|
+
}
|
|
35
|
+
|
|
36
|
+
/**
|
|
37
|
+
* Returns true if `selectionInBuilder` causes `selectionToTest` to be selected.
|
|
38
|
+
*
|
|
39
|
+
* Examples that return true:
|
|
40
|
+
*
|
|
41
|
+
* doesSelect(Person.query(), '*', 'name')
|
|
42
|
+
* doesSelect(Person.query(), 'Person.*', 'name')
|
|
43
|
+
* doesSelect(Person.query(), 'name', 'name')
|
|
44
|
+
* doesSelect(Person.query(), 'name', 'Person.name')
|
|
45
|
+
*/
|
|
46
|
+
static doesSelect(builder, selectionInBuilder, selectionToTest) {
|
|
47
|
+
selectionInBuilder = Selection.create(selectionInBuilder);
|
|
48
|
+
selectionToTest = Selection.create(selectionToTest);
|
|
49
|
+
|
|
50
|
+
if (selectionInBuilder.column === '*') {
|
|
51
|
+
if (selectionInBuilder.table) {
|
|
52
|
+
if (selectionToTest.column === '*') {
|
|
53
|
+
return selectionToTest.table === selectionInBuilder.table;
|
|
54
|
+
} else {
|
|
55
|
+
return (
|
|
56
|
+
selectionToTest.table === null || selectionToTest.table === selectionInBuilder.table
|
|
57
|
+
);
|
|
58
|
+
}
|
|
59
|
+
} else {
|
|
60
|
+
return true;
|
|
61
|
+
}
|
|
62
|
+
} else {
|
|
63
|
+
const selectionInBuilderTable = selectionInBuilder.table || builder.tableRef();
|
|
64
|
+
|
|
65
|
+
if (selectionToTest.column === '*') {
|
|
66
|
+
return false;
|
|
67
|
+
} else {
|
|
68
|
+
return (
|
|
69
|
+
selectionToTest.column === selectionInBuilder.column &&
|
|
70
|
+
(selectionToTest.table === null || selectionToTest.table === selectionInBuilderTable)
|
|
71
|
+
);
|
|
72
|
+
}
|
|
73
|
+
}
|
|
74
|
+
}
|
|
75
|
+
}
|
|
76
|
+
|
|
77
|
+
Object.defineProperties(Selection.prototype, {
|
|
78
|
+
isObjectionSelection: {
|
|
79
|
+
enumerable: false,
|
|
80
|
+
writable: false,
|
|
81
|
+
value: true,
|
|
82
|
+
},
|
|
83
|
+
});
|
|
84
|
+
|
|
85
|
+
function createSelectionFromReference(ref) {
|
|
86
|
+
return new Selection(ref.tableName, ref.column, ref.alias);
|
|
87
|
+
}
|
|
88
|
+
|
|
89
|
+
function createSelectionFromRaw(raw) {
|
|
90
|
+
if (raw.alias) {
|
|
91
|
+
return new Selection(null, null, raw.alias);
|
|
92
|
+
} else {
|
|
93
|
+
return null;
|
|
94
|
+
}
|
|
95
|
+
}
|
|
96
|
+
|
|
97
|
+
function createSelectionFromString(selection) {
|
|
98
|
+
let table = null;
|
|
99
|
+
let column = null;
|
|
100
|
+
let alias = null;
|
|
101
|
+
|
|
102
|
+
if (ALIAS_REGEX.test(selection)) {
|
|
103
|
+
const parts = selection.split(ALIAS_REGEX);
|
|
104
|
+
|
|
105
|
+
selection = parts[0].trim();
|
|
106
|
+
alias = parts[1].trim();
|
|
107
|
+
}
|
|
108
|
+
|
|
109
|
+
const dotIdx = selection.lastIndexOf('.');
|
|
110
|
+
|
|
111
|
+
if (dotIdx !== -1) {
|
|
112
|
+
table = selection.substr(0, dotIdx);
|
|
113
|
+
column = selection.substr(dotIdx + 1);
|
|
114
|
+
} else {
|
|
115
|
+
column = selection;
|
|
116
|
+
}
|
|
117
|
+
|
|
118
|
+
return new Selection(table, column, alias);
|
|
119
|
+
}
|
|
120
|
+
|
|
121
|
+
module.exports = {
|
|
122
|
+
Selection,
|
|
123
|
+
};
|
|
@@ -0,0 +1,117 @@
|
|
|
1
|
+
'use strict';
|
|
2
|
+
|
|
3
|
+
const { ObjectionToKnexConvertingOperation } = require('../ObjectionToKnexConvertingOperation');
|
|
4
|
+
const { flatten, zipObject, isString } = require('../../../utils/objectUtils');
|
|
5
|
+
const { getTempColumn } = require('../../../utils/tmpColumnUtils');
|
|
6
|
+
|
|
7
|
+
class WhereInCompositeMsSqlOperation extends ObjectionToKnexConvertingOperation {
|
|
8
|
+
constructor(name, opt) {
|
|
9
|
+
super(name, opt);
|
|
10
|
+
this.prefix = this.opt.prefix || null;
|
|
11
|
+
}
|
|
12
|
+
|
|
13
|
+
onBuildKnex(knexBuilder, builder) {
|
|
14
|
+
const args = this.getKnexArgs(builder);
|
|
15
|
+
return this.build(builder.knex(), knexBuilder, args[0], args[1]);
|
|
16
|
+
}
|
|
17
|
+
|
|
18
|
+
build(knex, knexBuilder, columns, values) {
|
|
19
|
+
let isCompositeKey = Array.isArray(columns) && columns.length > 1;
|
|
20
|
+
|
|
21
|
+
if (isCompositeKey) {
|
|
22
|
+
return this.buildComposite(knex, knexBuilder, columns, values);
|
|
23
|
+
} else {
|
|
24
|
+
return this.buildNonComposite(knexBuilder, columns, values);
|
|
25
|
+
}
|
|
26
|
+
}
|
|
27
|
+
|
|
28
|
+
buildComposite(knex, knexBuilder, columns, values) {
|
|
29
|
+
const helperColumns = columns.map((_, index) => getTempColumn(index));
|
|
30
|
+
|
|
31
|
+
if (Array.isArray(values)) {
|
|
32
|
+
return this.buildCompositeValue(knex, knexBuilder, columns, helperColumns, values);
|
|
33
|
+
} else {
|
|
34
|
+
return this.buildCompositeSubquery(
|
|
35
|
+
knex,
|
|
36
|
+
knexBuilder,
|
|
37
|
+
columns,
|
|
38
|
+
helperColumns,
|
|
39
|
+
values.as(knex.raw(`V(${helperColumns.map((_) => '??')})`, helperColumns)),
|
|
40
|
+
);
|
|
41
|
+
}
|
|
42
|
+
}
|
|
43
|
+
|
|
44
|
+
buildCompositeValue(knex, knexBuilder, columns, helperColumns, values) {
|
|
45
|
+
return this.buildCompositeSubquery(
|
|
46
|
+
knex,
|
|
47
|
+
knexBuilder,
|
|
48
|
+
columns,
|
|
49
|
+
helperColumns,
|
|
50
|
+
knex.raw(
|
|
51
|
+
`(VALUES ${values
|
|
52
|
+
.map((value) => `(${value.map((_) => '?').join(',')})`)
|
|
53
|
+
.join(',')}) AS V(${helperColumns.map((_) => '??').join(',')})`,
|
|
54
|
+
flatten(values).concat(helperColumns),
|
|
55
|
+
),
|
|
56
|
+
);
|
|
57
|
+
}
|
|
58
|
+
|
|
59
|
+
buildCompositeSubquery(knex, knexBuilder, columns, helperColumns, subQuery) {
|
|
60
|
+
const wrapperQuery = knex.from(subQuery).where(
|
|
61
|
+
zipObject(
|
|
62
|
+
helperColumns,
|
|
63
|
+
columns.map((column) => knex.raw('??', column)),
|
|
64
|
+
),
|
|
65
|
+
);
|
|
66
|
+
|
|
67
|
+
if (this.prefix === 'not') {
|
|
68
|
+
return knexBuilder.whereNotExists(wrapperQuery);
|
|
69
|
+
} else {
|
|
70
|
+
return knexBuilder.whereExists(wrapperQuery);
|
|
71
|
+
}
|
|
72
|
+
}
|
|
73
|
+
|
|
74
|
+
buildNonComposite(knexBuilder, columns, values) {
|
|
75
|
+
const col = isString(columns) ? columns : columns[0];
|
|
76
|
+
|
|
77
|
+
if (Array.isArray(values)) {
|
|
78
|
+
values = pickNonNull(values, []);
|
|
79
|
+
} else {
|
|
80
|
+
values = [values];
|
|
81
|
+
}
|
|
82
|
+
|
|
83
|
+
return this.whereIn(knexBuilder, col, values);
|
|
84
|
+
}
|
|
85
|
+
|
|
86
|
+
whereIn(knexBuilder, col, val) {
|
|
87
|
+
if (this.prefix === 'not') {
|
|
88
|
+
return knexBuilder.whereNotIn(col, val);
|
|
89
|
+
} else {
|
|
90
|
+
return knexBuilder.whereIn(col, val);
|
|
91
|
+
}
|
|
92
|
+
}
|
|
93
|
+
|
|
94
|
+
clone() {
|
|
95
|
+
const clone = super.clone();
|
|
96
|
+
clone.prefix = this.prefix;
|
|
97
|
+
return clone;
|
|
98
|
+
}
|
|
99
|
+
}
|
|
100
|
+
|
|
101
|
+
function pickNonNull(values, output) {
|
|
102
|
+
for (let i = 0, l = values.length; i < l; ++i) {
|
|
103
|
+
const val = values[i];
|
|
104
|
+
|
|
105
|
+
if (Array.isArray(val)) {
|
|
106
|
+
pickNonNull(val, output);
|
|
107
|
+
} else if (val !== null && val !== undefined) {
|
|
108
|
+
output.push(val);
|
|
109
|
+
}
|
|
110
|
+
}
|
|
111
|
+
|
|
112
|
+
return output;
|
|
113
|
+
}
|
|
114
|
+
|
|
115
|
+
module.exports = {
|
|
116
|
+
WhereInCompositeMsSqlOperation,
|
|
117
|
+
};
|
|
@@ -0,0 +1,100 @@
|
|
|
1
|
+
'use strict';
|
|
2
|
+
|
|
3
|
+
const { ObjectionToKnexConvertingOperation } = require('../ObjectionToKnexConvertingOperation');
|
|
4
|
+
const { isObject, asSingle } = require('../../../utils/objectUtils');
|
|
5
|
+
const { isKnexQueryBuilder } = require('../../../utils/knexUtils');
|
|
6
|
+
|
|
7
|
+
class WhereInCompositeOperation extends ObjectionToKnexConvertingOperation {
|
|
8
|
+
constructor(name, opt) {
|
|
9
|
+
super(name, opt);
|
|
10
|
+
this.prefix = this.opt.prefix || null;
|
|
11
|
+
}
|
|
12
|
+
|
|
13
|
+
onBuildKnex(knexBuilder, builder) {
|
|
14
|
+
const whereInArgs = buildWhereInArgs(builder.knex(), ...this.getKnexArgs(builder));
|
|
15
|
+
|
|
16
|
+
if (this.prefix === 'not') {
|
|
17
|
+
return knexBuilder.whereNotIn(...whereInArgs);
|
|
18
|
+
} else {
|
|
19
|
+
return knexBuilder.whereIn(...whereInArgs);
|
|
20
|
+
}
|
|
21
|
+
}
|
|
22
|
+
|
|
23
|
+
clone() {
|
|
24
|
+
const clone = super.clone();
|
|
25
|
+
clone.prefix = this.prefix;
|
|
26
|
+
return clone;
|
|
27
|
+
}
|
|
28
|
+
}
|
|
29
|
+
|
|
30
|
+
function buildWhereInArgs(knex, columns, values) {
|
|
31
|
+
if (isCompositeKey(columns)) {
|
|
32
|
+
return buildCompositeArgs(knex, columns, values);
|
|
33
|
+
} else {
|
|
34
|
+
return buildNonCompositeArgs(columns, values);
|
|
35
|
+
}
|
|
36
|
+
}
|
|
37
|
+
|
|
38
|
+
function isCompositeKey(columns) {
|
|
39
|
+
return Array.isArray(columns) && columns.length > 1;
|
|
40
|
+
}
|
|
41
|
+
|
|
42
|
+
function buildCompositeArgs(knex, columns, values) {
|
|
43
|
+
if (Array.isArray(values)) {
|
|
44
|
+
return buildCompositeValueArgs(columns, values);
|
|
45
|
+
} else {
|
|
46
|
+
return buildCompositeSubqueryArgs(knex, columns, values);
|
|
47
|
+
}
|
|
48
|
+
}
|
|
49
|
+
|
|
50
|
+
function buildCompositeValueArgs(columns, values) {
|
|
51
|
+
if (!Array.isArray(values[0])) {
|
|
52
|
+
return [columns, [values]];
|
|
53
|
+
} else {
|
|
54
|
+
return [columns, values];
|
|
55
|
+
}
|
|
56
|
+
}
|
|
57
|
+
|
|
58
|
+
function buildCompositeSubqueryArgs(knex, columns, subquery) {
|
|
59
|
+
const sql = `(${columns
|
|
60
|
+
.map((col) => {
|
|
61
|
+
// On older versions of knex, raw doesn't work
|
|
62
|
+
// with `??`. We use `?` for those.
|
|
63
|
+
if (isObject(col)) {
|
|
64
|
+
return '?';
|
|
65
|
+
} else {
|
|
66
|
+
return '??';
|
|
67
|
+
}
|
|
68
|
+
})
|
|
69
|
+
.join(',')})`;
|
|
70
|
+
|
|
71
|
+
return [knex.raw(sql, columns), subquery];
|
|
72
|
+
}
|
|
73
|
+
|
|
74
|
+
function buildNonCompositeArgs(columns, values) {
|
|
75
|
+
if (Array.isArray(values)) {
|
|
76
|
+
values = pickNonNull(values, []);
|
|
77
|
+
} else if (!isKnexQueryBuilder(values)) {
|
|
78
|
+
values = [values];
|
|
79
|
+
}
|
|
80
|
+
|
|
81
|
+
return [asSingle(columns), values];
|
|
82
|
+
}
|
|
83
|
+
|
|
84
|
+
function pickNonNull(values, output) {
|
|
85
|
+
for (let i = 0, l = values.length; i < l; ++i) {
|
|
86
|
+
const val = values[i];
|
|
87
|
+
|
|
88
|
+
if (Array.isArray(val)) {
|
|
89
|
+
pickNonNull(val, output);
|
|
90
|
+
} else if (val !== null && val !== undefined) {
|
|
91
|
+
output.push(val);
|
|
92
|
+
}
|
|
93
|
+
}
|
|
94
|
+
|
|
95
|
+
return output;
|
|
96
|
+
}
|
|
97
|
+
|
|
98
|
+
module.exports = {
|
|
99
|
+
WhereInCompositeOperation,
|
|
100
|
+
};
|
|
@@ -0,0 +1,104 @@
|
|
|
1
|
+
'use strict';
|
|
2
|
+
|
|
3
|
+
const { ObjectionToKnexConvertingOperation } = require('../ObjectionToKnexConvertingOperation');
|
|
4
|
+
const { isKnexQueryBuilder } = require('../../../utils/knexUtils');
|
|
5
|
+
const { asSingle } = require('../../../utils/objectUtils');
|
|
6
|
+
|
|
7
|
+
class WhereInCompositeSqliteOperation extends ObjectionToKnexConvertingOperation {
|
|
8
|
+
constructor(name, opt) {
|
|
9
|
+
super(name, opt);
|
|
10
|
+
this.prefix = this.opt.prefix || null;
|
|
11
|
+
}
|
|
12
|
+
|
|
13
|
+
onBuildKnex(knexBuilder, builder) {
|
|
14
|
+
const { method, args } = buildWhereArgs(...this.getKnexArgs(builder));
|
|
15
|
+
|
|
16
|
+
if (method === 'where') {
|
|
17
|
+
if (this.prefix === 'not') {
|
|
18
|
+
return knexBuilder.whereNot(...args);
|
|
19
|
+
} else {
|
|
20
|
+
return knexBuilder.where(...args);
|
|
21
|
+
}
|
|
22
|
+
} else {
|
|
23
|
+
if (this.prefix === 'not') {
|
|
24
|
+
return knexBuilder.whereNotIn(...args);
|
|
25
|
+
} else {
|
|
26
|
+
return knexBuilder.whereIn(...args);
|
|
27
|
+
}
|
|
28
|
+
}
|
|
29
|
+
}
|
|
30
|
+
|
|
31
|
+
clone() {
|
|
32
|
+
const clone = super.clone();
|
|
33
|
+
clone.prefix = this.prefix;
|
|
34
|
+
return clone;
|
|
35
|
+
}
|
|
36
|
+
}
|
|
37
|
+
|
|
38
|
+
function buildWhereArgs(columns, values) {
|
|
39
|
+
if (isCompositeKey(columns)) {
|
|
40
|
+
return buildCompositeArgs(columns, values);
|
|
41
|
+
} else {
|
|
42
|
+
return buildNonCompositeArgs(columns, values);
|
|
43
|
+
}
|
|
44
|
+
}
|
|
45
|
+
|
|
46
|
+
function isCompositeKey(columns) {
|
|
47
|
+
return Array.isArray(columns) && columns.length > 1;
|
|
48
|
+
}
|
|
49
|
+
|
|
50
|
+
function buildCompositeArgs(columns, values) {
|
|
51
|
+
if (!Array.isArray(values)) {
|
|
52
|
+
// If the `values` is not an array of values but a function or a subquery
|
|
53
|
+
// we have no way to implement this method.
|
|
54
|
+
throw new Error(`sqlite doesn't support multi-column where in clauses`);
|
|
55
|
+
}
|
|
56
|
+
|
|
57
|
+
// Sqlite doesn't support the `where in` syntax for multiple columns but
|
|
58
|
+
// we can emulate it using grouped `or` clauses.
|
|
59
|
+
return {
|
|
60
|
+
method: 'where',
|
|
61
|
+
args: [
|
|
62
|
+
(builder) => {
|
|
63
|
+
values.forEach((val) => {
|
|
64
|
+
builder.orWhere((builder) => {
|
|
65
|
+
columns.forEach((col, idx) => {
|
|
66
|
+
builder.andWhere(col, val[idx]);
|
|
67
|
+
});
|
|
68
|
+
});
|
|
69
|
+
});
|
|
70
|
+
},
|
|
71
|
+
],
|
|
72
|
+
};
|
|
73
|
+
}
|
|
74
|
+
|
|
75
|
+
function buildNonCompositeArgs(columns, values) {
|
|
76
|
+
if (Array.isArray(values)) {
|
|
77
|
+
values = pickNonNull(values, []);
|
|
78
|
+
} else if (!isKnexQueryBuilder(values)) {
|
|
79
|
+
values = [values];
|
|
80
|
+
}
|
|
81
|
+
|
|
82
|
+
return {
|
|
83
|
+
method: 'whereIn',
|
|
84
|
+
args: [asSingle(columns), values],
|
|
85
|
+
};
|
|
86
|
+
}
|
|
87
|
+
|
|
88
|
+
function pickNonNull(values, output) {
|
|
89
|
+
for (let i = 0, l = values.length; i < l; ++i) {
|
|
90
|
+
const val = values[i];
|
|
91
|
+
|
|
92
|
+
if (Array.isArray(val)) {
|
|
93
|
+
pickNonNull(val, output);
|
|
94
|
+
} else if (val !== null && val !== undefined) {
|
|
95
|
+
output.push(val);
|
|
96
|
+
}
|
|
97
|
+
}
|
|
98
|
+
|
|
99
|
+
return output;
|
|
100
|
+
}
|
|
101
|
+
|
|
102
|
+
module.exports = {
|
|
103
|
+
WhereInCompositeSqliteOperation,
|
|
104
|
+
};
|