@mikro-orm/knex 6.0.0-dev.1 → 6.0.0-dev.10
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/{dist/AbstractSqlConnection.d.ts → AbstractSqlConnection.d.ts} +0 -0
- package/{dist/AbstractSqlConnection.js → AbstractSqlConnection.js} +5 -0
- package/AbstractSqlDriver.d.ts +66 -0
- package/{dist/AbstractSqlDriver.js → AbstractSqlDriver.js} +218 -70
- package/{dist/AbstractSqlPlatform.d.ts → AbstractSqlPlatform.d.ts} +7 -7
- package/{dist/AbstractSqlPlatform.js → AbstractSqlPlatform.js} +12 -14
- package/{dist/MonkeyPatchable.d.ts → MonkeyPatchable.d.ts} +0 -0
- package/{dist/MonkeyPatchable.js → MonkeyPatchable.js} +0 -0
- package/{dist/README.md → README.md} +2 -2
- package/{dist/SqlEntityManager.d.ts → SqlEntityManager.d.ts} +5 -5
- package/{dist/SqlEntityManager.js → SqlEntityManager.js} +0 -1
- package/{dist/SqlEntityRepository.d.ts → SqlEntityRepository.d.ts} +3 -3
- package/{dist/SqlEntityRepository.js → SqlEntityRepository.js} +1 -1
- package/{dist/index.d.ts → index.d.ts} +1 -0
- package/{dist/index.js → index.js} +6 -1
- package/index.mjs +192 -0
- package/package.json +18 -47
- package/{dist/query → query}/ArrayCriteriaNode.d.ts +0 -0
- package/{dist/query → query}/ArrayCriteriaNode.js +0 -0
- package/{dist/query → query}/CriteriaNode.d.ts +0 -0
- package/{dist/query → query}/CriteriaNode.js +7 -4
- package/{dist/query → query}/CriteriaNodeFactory.d.ts +0 -0
- package/{dist/query → query}/CriteriaNodeFactory.js +0 -0
- package/{dist/query → query}/ObjectCriteriaNode.d.ts +0 -0
- package/{dist/query → query}/ObjectCriteriaNode.js +14 -1
- package/{dist/query → query}/QueryBuilder.d.ts +58 -24
- package/{dist/query → query}/QueryBuilder.js +285 -84
- package/{dist/query → query}/QueryBuilderHelper.d.ts +19 -11
- package/{dist/query → query}/QueryBuilderHelper.js +206 -108
- package/{dist/query → query}/ScalarCriteriaNode.d.ts +0 -0
- package/{dist/query → query}/ScalarCriteriaNode.js +0 -0
- package/{dist/query → query}/enums.d.ts +0 -0
- package/{dist/query → query}/enums.js +0 -0
- package/{dist/query → query}/index.d.ts +0 -0
- package/{dist/query → query}/index.js +5 -1
- package/{dist/schema → schema}/DatabaseSchema.d.ts +1 -1
- package/{dist/schema → schema}/DatabaseSchema.js +8 -16
- package/{dist/schema → schema}/DatabaseTable.d.ts +8 -8
- package/{dist/schema → schema}/DatabaseTable.js +2 -1
- package/{dist/schema → schema}/SchemaComparator.d.ts +7 -5
- package/{dist/schema → schema}/SchemaComparator.js +58 -10
- package/{dist/schema → schema}/SchemaHelper.d.ts +15 -10
- package/{dist/schema → schema}/SchemaHelper.js +46 -8
- package/{dist/schema/SchemaGenerator.d.ts → schema/SqlSchemaGenerator.d.ts} +12 -12
- package/{dist/schema/SchemaGenerator.js → schema/SqlSchemaGenerator.js} +85 -34
- package/{dist/schema → schema}/index.d.ts +1 -1
- package/{dist/schema → schema}/index.js +6 -2
- package/{dist/typings.d.ts → typings.d.ts} +16 -12
- package/{dist/typings.js → typings.js} +0 -0
- package/CHANGELOG.md +0 -661
- package/dist/AbstractSqlDriver.d.ts +0 -55
- package/dist/LICENSE +0 -21
- package/dist/index.mjs +0 -26
- package/dist/package.json +0 -99
|
@@ -1,8 +1,9 @@
|
|
|
1
1
|
import type { Knex } from 'knex';
|
|
2
|
-
import type { Dictionary, EntityData, EntityMetadata, EntityProperty, FlatQueryOrderMap,
|
|
2
|
+
import type { Dictionary, EntityData, EntityMetadata, EntityProperty, FlatQueryOrderMap, QBFilterQuery } from '@mikro-orm/core';
|
|
3
3
|
import { LockMode } from '@mikro-orm/core';
|
|
4
4
|
import { QueryType } from './enums';
|
|
5
5
|
import type { Field, JoinOptions } from '../typings';
|
|
6
|
+
import type { AbstractSqlDriver } from '../AbstractSqlDriver';
|
|
6
7
|
/**
|
|
7
8
|
* @internal
|
|
8
9
|
*/
|
|
@@ -11,25 +12,28 @@ export declare class QueryBuilderHelper {
|
|
|
11
12
|
private readonly alias;
|
|
12
13
|
private readonly aliasMap;
|
|
13
14
|
private readonly subQueries;
|
|
14
|
-
private readonly metadata;
|
|
15
15
|
private readonly knex;
|
|
16
|
+
private readonly driver;
|
|
16
17
|
private readonly platform;
|
|
17
|
-
|
|
18
|
-
|
|
19
|
-
mapper(field: string, type?: QueryType
|
|
18
|
+
private readonly metadata;
|
|
19
|
+
constructor(entityName: string, alias: string, aliasMap: Dictionary<Alias>, subQueries: Dictionary<string>, knex: Knex, driver: AbstractSqlDriver);
|
|
20
|
+
mapper(field: string | Knex.Raw, type?: QueryType): string;
|
|
21
|
+
mapper(field: string | Knex.Raw, type?: QueryType, value?: any, alias?: string | null): string;
|
|
20
22
|
processData(data: Dictionary, convertCustomTypes: boolean, multi?: boolean): any;
|
|
21
23
|
joinOneToReference(prop: EntityProperty, ownerAlias: string, alias: string, type: 'leftJoin' | 'innerJoin' | 'pivotJoin', cond?: Dictionary): JoinOptions;
|
|
22
24
|
joinManyToOneReference(prop: EntityProperty, ownerAlias: string, alias: string, type: 'leftJoin' | 'innerJoin' | 'pivotJoin', cond?: Dictionary): JoinOptions;
|
|
23
25
|
joinManyToManyReference(prop: EntityProperty, ownerAlias: string, alias: string, pivotAlias: string, type: 'leftJoin' | 'innerJoin' | 'pivotJoin', cond: Dictionary, path: string): Dictionary<JoinOptions>;
|
|
24
26
|
joinPivotTable(field: string, prop: EntityProperty, ownerAlias: string, alias: string, type: 'leftJoin' | 'innerJoin' | 'pivotJoin', cond?: Dictionary): JoinOptions;
|
|
25
27
|
processJoins(qb: Knex.QueryBuilder, joins: Dictionary<JoinOptions>, schema?: string): void;
|
|
28
|
+
private processJoinClause;
|
|
29
|
+
private wrapQueryGroup;
|
|
26
30
|
mapJoinColumns(type: QueryType, join: JoinOptions): (string | Knex.Raw)[];
|
|
27
31
|
isOneToOneInverse(field: string): boolean;
|
|
28
32
|
getTableName(entityName: string): string;
|
|
29
33
|
/**
|
|
30
34
|
* Checks whether the RE can be rewritten to simple LIKE query
|
|
31
35
|
*/
|
|
32
|
-
isSimpleRegExp(re: any):
|
|
36
|
+
isSimpleRegExp(re: any): re is RegExp;
|
|
33
37
|
getRegExpParam(re: RegExp): string;
|
|
34
38
|
appendOnConflictClause<T>(type: QueryType, onConflict: {
|
|
35
39
|
fields: string[];
|
|
@@ -42,13 +46,10 @@ export declare class QueryBuilderHelper {
|
|
|
42
46
|
private processCustomExpression;
|
|
43
47
|
private processObjectSubCondition;
|
|
44
48
|
private getOperatorReplacement;
|
|
45
|
-
private appendJoinClause;
|
|
46
|
-
private appendJoinSubClause;
|
|
47
|
-
private processObjectSubClause;
|
|
48
49
|
getQueryOrder(type: QueryType, orderBy: FlatQueryOrderMap | FlatQueryOrderMap[], populate: Dictionary<string>): string;
|
|
49
50
|
getQueryOrderFromObject(type: QueryType, orderBy: FlatQueryOrderMap, populate: Dictionary<string>): string;
|
|
50
51
|
finalize(type: QueryType, qb: Knex.QueryBuilder, meta?: EntityMetadata): void;
|
|
51
|
-
splitField(field: string): [string, string];
|
|
52
|
+
splitField(field: string, greedyAlias?: boolean): [string, string];
|
|
52
53
|
getLockSQL(qb: Knex.QueryBuilder, lockMode: LockMode, lockTables?: string[]): void;
|
|
53
54
|
updateVersionProperty(qb: Knex.QueryBuilder, data: Dictionary): void;
|
|
54
55
|
static isCustomExpression(field: string, hasAlias?: boolean): boolean;
|
|
@@ -57,5 +58,12 @@ export declare class QueryBuilderHelper {
|
|
|
57
58
|
private isPrefixed;
|
|
58
59
|
private fieldName;
|
|
59
60
|
getProperty(field: string, alias?: string): EntityProperty | undefined;
|
|
60
|
-
isTableNameAliasRequired(type
|
|
61
|
+
isTableNameAliasRequired(type?: QueryType): boolean;
|
|
62
|
+
private mapData;
|
|
63
|
+
}
|
|
64
|
+
export interface Alias {
|
|
65
|
+
aliasName: string;
|
|
66
|
+
entityName: string;
|
|
67
|
+
metadata?: EntityMetadata;
|
|
68
|
+
subQuery?: Knex.QueryBuilder;
|
|
61
69
|
}
|
|
@@ -8,16 +8,20 @@ const enums_1 = require("./enums");
|
|
|
8
8
|
* @internal
|
|
9
9
|
*/
|
|
10
10
|
class QueryBuilderHelper {
|
|
11
|
-
constructor(entityName, alias, aliasMap, subQueries,
|
|
11
|
+
constructor(entityName, alias, aliasMap, subQueries, knex, driver) {
|
|
12
12
|
this.entityName = entityName;
|
|
13
13
|
this.alias = alias;
|
|
14
14
|
this.aliasMap = aliasMap;
|
|
15
15
|
this.subQueries = subQueries;
|
|
16
|
-
this.metadata = metadata;
|
|
17
16
|
this.knex = knex;
|
|
18
|
-
this.
|
|
17
|
+
this.driver = driver;
|
|
18
|
+
this.platform = this.driver.getPlatform();
|
|
19
|
+
this.metadata = this.driver.getMetadata();
|
|
19
20
|
}
|
|
20
21
|
mapper(field, type = enums_1.QueryType.SELECT, value, alias) {
|
|
22
|
+
if (typeof field !== 'string') {
|
|
23
|
+
return field;
|
|
24
|
+
}
|
|
21
25
|
const isTableNameAliasRequired = this.isTableNameAliasRequired(type);
|
|
22
26
|
const fields = core_1.Utils.splitPrimaryKeys(field);
|
|
23
27
|
if (fields.length > 1) {
|
|
@@ -26,10 +30,10 @@ class QueryBuilderHelper {
|
|
|
26
30
|
const [a, f] = this.splitField(p);
|
|
27
31
|
const prop = this.getProperty(f, a);
|
|
28
32
|
if (prop) {
|
|
29
|
-
parts.push(...prop.fieldNames.map(f => this.mapper(f, type, value, alias)));
|
|
33
|
+
parts.push(...prop.fieldNames.map(f => this.mapper(a !== this.alias ? `${a}.${f}` : f, type, value, alias)));
|
|
30
34
|
}
|
|
31
35
|
else {
|
|
32
|
-
parts.push(this.mapper(`${a}.${f}
|
|
36
|
+
parts.push(this.mapper(a !== this.alias ? `${a}.${f}` : f, type, value, alias));
|
|
33
37
|
}
|
|
34
38
|
}
|
|
35
39
|
return this.knex.raw('(' + parts.map(part => this.knex.ref(part)).join(', ') + ')');
|
|
@@ -38,6 +42,10 @@ class QueryBuilderHelper {
|
|
|
38
42
|
const customExpression = QueryBuilderHelper.isCustomExpression(field, !!alias);
|
|
39
43
|
const [a, f] = this.splitField(field);
|
|
40
44
|
const prop = this.getProperty(f, a);
|
|
45
|
+
// embeddable nested path instead of a regular property with table alias, reset alias
|
|
46
|
+
if (prop?.name === a && prop.embeddedProps[f]) {
|
|
47
|
+
return this.alias + '.' + prop.fieldNames[0];
|
|
48
|
+
}
|
|
41
49
|
const noPrefix = prop && prop.persist === false;
|
|
42
50
|
if (prop?.fieldNameRaw) {
|
|
43
51
|
return this.knex.raw(this.prefix(field, isTableNameAliasRequired));
|
|
@@ -75,47 +83,23 @@ class QueryBuilderHelper {
|
|
|
75
83
|
if (Array.isArray(data)) {
|
|
76
84
|
return data.map(d => this.processData(d, convertCustomTypes, true));
|
|
77
85
|
}
|
|
78
|
-
data = Object.assign({}, data); // copy first
|
|
79
86
|
const meta = this.metadata.find(this.entityName);
|
|
80
|
-
|
|
81
|
-
if (!meta?.properties[k]) {
|
|
82
|
-
return;
|
|
83
|
-
}
|
|
84
|
-
const prop = meta.properties[k];
|
|
85
|
-
if (prop.joinColumns && Array.isArray(data[k])) {
|
|
86
|
-
const copy = data[k];
|
|
87
|
-
delete data[k];
|
|
88
|
-
prop.joinColumns.forEach((joinColumn, idx) => data[joinColumn] = copy[idx]);
|
|
89
|
-
return;
|
|
90
|
-
}
|
|
91
|
-
if (prop.customType && convertCustomTypes && !this.platform.isRaw(data[k])) {
|
|
92
|
-
data[k] = prop.customType.convertToDatabaseValue(data[k], this.platform, true);
|
|
93
|
-
}
|
|
94
|
-
if (prop.customType && 'convertToDatabaseValueSQL' in prop.customType && !this.platform.isRaw(data[k])) {
|
|
95
|
-
const quoted = this.platform.quoteValue(data[k]);
|
|
96
|
-
data[k] = this.knex.raw(prop.customType.convertToDatabaseValueSQL(quoted, this.platform));
|
|
97
|
-
}
|
|
98
|
-
if (!prop.customType && (Array.isArray(data[k]) || core_1.Utils.isPlainObject(data[k]))) {
|
|
99
|
-
data[k] = JSON.stringify(data[k]);
|
|
100
|
-
}
|
|
101
|
-
if (prop.fieldNames) {
|
|
102
|
-
core_1.Utils.renameKey(data, k, prop.fieldNames[0]);
|
|
103
|
-
}
|
|
104
|
-
});
|
|
87
|
+
data = this.mapData(data, meta?.properties, convertCustomTypes);
|
|
105
88
|
if (!core_1.Utils.hasObjectKeys(data) && meta && multi) {
|
|
89
|
+
/* istanbul ignore next */
|
|
106
90
|
data[meta.primaryKeys[0]] = this.platform.usesDefaultKeyword() ? this.knex.raw('default') : undefined;
|
|
107
91
|
}
|
|
108
92
|
return data;
|
|
109
93
|
}
|
|
110
94
|
joinOneToReference(prop, ownerAlias, alias, type, cond = {}) {
|
|
111
|
-
const
|
|
112
|
-
const prop2 = meta.properties[prop.mappedBy || prop.inversedBy];
|
|
95
|
+
const prop2 = prop.targetMeta.properties[prop.mappedBy || prop.inversedBy];
|
|
113
96
|
const table = this.getTableName(prop.type);
|
|
97
|
+
const schema = this.driver.getSchemaName(prop.targetMeta);
|
|
114
98
|
const joinColumns = prop.owner ? prop.referencedColumnNames : prop2.joinColumns;
|
|
115
99
|
const inverseJoinColumns = prop.referencedColumnNames;
|
|
116
100
|
const primaryKeys = prop.owner ? prop.joinColumns : prop2.referencedColumnNames;
|
|
117
101
|
return {
|
|
118
|
-
prop, type, cond, ownerAlias, alias, table,
|
|
102
|
+
prop, type, cond, ownerAlias, alias, table, schema,
|
|
119
103
|
joinColumns, inverseJoinColumns, primaryKeys,
|
|
120
104
|
};
|
|
121
105
|
}
|
|
@@ -123,11 +107,13 @@ class QueryBuilderHelper {
|
|
|
123
107
|
return {
|
|
124
108
|
prop, type, cond, ownerAlias, alias,
|
|
125
109
|
table: this.getTableName(prop.type),
|
|
110
|
+
schema: this.driver.getSchemaName(prop.targetMeta),
|
|
126
111
|
joinColumns: prop.referencedColumnNames,
|
|
127
112
|
primaryKeys: prop.fieldNames,
|
|
128
113
|
};
|
|
129
114
|
}
|
|
130
115
|
joinManyToManyReference(prop, ownerAlias, alias, pivotAlias, type, cond, path) {
|
|
116
|
+
const pivotMeta = this.metadata.find(prop.pivotEntity);
|
|
131
117
|
const ret = {
|
|
132
118
|
[`${ownerAlias}.${prop.name}#${pivotAlias}`]: {
|
|
133
119
|
prop, type, cond, ownerAlias,
|
|
@@ -136,21 +122,22 @@ class QueryBuilderHelper {
|
|
|
136
122
|
joinColumns: prop.joinColumns,
|
|
137
123
|
inverseJoinColumns: prop.inverseJoinColumns,
|
|
138
124
|
primaryKeys: prop.referencedColumnNames,
|
|
139
|
-
table:
|
|
125
|
+
table: pivotMeta.tableName,
|
|
126
|
+
schema: this.driver.getSchemaName(pivotMeta),
|
|
140
127
|
path: path.endsWith('[pivot]') ? path : `${path}[pivot]`,
|
|
141
128
|
},
|
|
142
129
|
};
|
|
143
130
|
if (type === 'pivotJoin') {
|
|
144
131
|
return ret;
|
|
145
132
|
}
|
|
146
|
-
const prop2 =
|
|
133
|
+
const prop2 = prop.owner ? pivotMeta.relations[1] : pivotMeta.relations[0];
|
|
147
134
|
ret[`${pivotAlias}.${prop2.name}#${alias}`] = this.joinManyToOneReference(prop2, pivotAlias, alias, type);
|
|
148
135
|
ret[`${pivotAlias}.${prop2.name}#${alias}`].path = path;
|
|
149
136
|
return ret;
|
|
150
137
|
}
|
|
151
138
|
joinPivotTable(field, prop, ownerAlias, alias, type, cond = {}) {
|
|
152
139
|
const pivotMeta = this.metadata.find(field);
|
|
153
|
-
const prop2 = pivotMeta.
|
|
140
|
+
const prop2 = pivotMeta.relations[0] === prop ? pivotMeta.relations[1] : pivotMeta.relations[0];
|
|
154
141
|
return {
|
|
155
142
|
prop, type, cond, ownerAlias, alias,
|
|
156
143
|
table: pivotMeta.collection,
|
|
@@ -163,21 +150,96 @@ class QueryBuilderHelper {
|
|
|
163
150
|
processJoins(qb, joins, schema) {
|
|
164
151
|
Object.values(joins).forEach(join => {
|
|
165
152
|
let table = `${join.table} as ${join.alias}`;
|
|
166
|
-
const method = join.type === '
|
|
153
|
+
const method = join.type === 'innerJoin' ? 'inner join' : 'left join';
|
|
167
154
|
schema = join.schema && join.schema !== '*' ? join.schema : schema;
|
|
168
155
|
if (schema) {
|
|
169
156
|
table = `${schema}.${table}`;
|
|
170
157
|
}
|
|
171
|
-
|
|
172
|
-
|
|
173
|
-
|
|
174
|
-
|
|
175
|
-
|
|
176
|
-
});
|
|
177
|
-
|
|
158
|
+
const conditions = [];
|
|
159
|
+
const params = [];
|
|
160
|
+
join.primaryKeys.forEach((primaryKey, idx) => {
|
|
161
|
+
const left = `${join.ownerAlias}.${primaryKey}`;
|
|
162
|
+
const right = `${join.alias}.${join.joinColumns[idx]}`;
|
|
163
|
+
conditions.push(`${this.knex.ref(left)} = ${this.knex.ref(right)}`);
|
|
164
|
+
});
|
|
165
|
+
Object.keys(join.cond).forEach(key => {
|
|
166
|
+
conditions.push(this.processJoinClause(key, join.cond[key], params));
|
|
178
167
|
});
|
|
168
|
+
return qb.joinRaw(`${method} ${this.knex.ref(table)} on ${conditions.join(' and ')}`, params);
|
|
179
169
|
});
|
|
180
170
|
}
|
|
171
|
+
processJoinClause(key, value, params, operator = '$eq') {
|
|
172
|
+
if (core_1.Utils.isGroupOperator(key) && Array.isArray(value)) {
|
|
173
|
+
const parts = value.map(sub => {
|
|
174
|
+
return this.wrapQueryGroup(Object.keys(sub).map(k => this.processJoinClause(k, sub[k], params)));
|
|
175
|
+
});
|
|
176
|
+
return this.wrapQueryGroup(parts, key);
|
|
177
|
+
}
|
|
178
|
+
if (this.isSimpleRegExp(value)) {
|
|
179
|
+
params.push(this.getRegExpParam(value));
|
|
180
|
+
return `${this.knex.ref(this.mapper(key))} like ?`;
|
|
181
|
+
}
|
|
182
|
+
if (value instanceof RegExp) {
|
|
183
|
+
value = this.platform.getRegExpValue(value);
|
|
184
|
+
}
|
|
185
|
+
if (core_1.Utils.isOperator(key, false) && core_1.Utils.isPlainObject(value)) {
|
|
186
|
+
const parts = Object.keys(value).map(k => this.processJoinClause(k, value[k], params, key));
|
|
187
|
+
return key === '$not' ? `not ${this.wrapQueryGroup(parts)}` : this.wrapQueryGroup(parts);
|
|
188
|
+
}
|
|
189
|
+
if (core_1.Utils.isPlainObject(value) && Object.keys(value).every(k => core_1.Utils.isOperator(k, false))) {
|
|
190
|
+
const parts = Object.keys(value).map(op => this.processJoinClause(key, value[op], params, op));
|
|
191
|
+
return this.wrapQueryGroup(parts);
|
|
192
|
+
}
|
|
193
|
+
operator = operator === '$not' ? '$eq' : operator;
|
|
194
|
+
if (value === null) {
|
|
195
|
+
return `${this.knex.ref(this.mapper(key))} is ${operator === '$ne' ? 'not ' : ''}null`;
|
|
196
|
+
}
|
|
197
|
+
if (operator === '$fulltext') {
|
|
198
|
+
const [fromAlias, fromField] = this.splitField(key);
|
|
199
|
+
const property = this.getProperty(fromField, fromAlias);
|
|
200
|
+
const query = this.knex.raw(this.platform.getFullTextWhereClause(property), {
|
|
201
|
+
column: this.mapper(key),
|
|
202
|
+
query: this.knex.raw('?'),
|
|
203
|
+
}).toSQL().toNative();
|
|
204
|
+
params.push(value);
|
|
205
|
+
return query.sql;
|
|
206
|
+
}
|
|
207
|
+
const replacement = this.getOperatorReplacement(operator, { [operator]: value });
|
|
208
|
+
if (['$in', '$nin'].includes(operator) && Array.isArray(value)) {
|
|
209
|
+
params.push(...value);
|
|
210
|
+
return `${this.knex.ref(this.mapper(key))} ${replacement} (${value.map(() => '?').join(', ')})`;
|
|
211
|
+
}
|
|
212
|
+
if (operator === '$exists') {
|
|
213
|
+
value = null;
|
|
214
|
+
}
|
|
215
|
+
if (QueryBuilderHelper.isCustomExpression(key)) {
|
|
216
|
+
// unwind parameters when ? found in field name
|
|
217
|
+
const count = key.concat('?').match(/\?/g).length - 1;
|
|
218
|
+
const values = core_1.Utils.asArray(value);
|
|
219
|
+
const params1 = values.slice(0, count).map((c) => core_1.Utils.isObject(c) ? JSON.stringify(c) : c);
|
|
220
|
+
const params2 = values.slice(count);
|
|
221
|
+
const k = this.mapper(key, enums_1.QueryType.SELECT, params1);
|
|
222
|
+
const { sql, bindings } = k.toSQL().toNative();
|
|
223
|
+
if (params2.length > 0) {
|
|
224
|
+
params.push(...bindings);
|
|
225
|
+
params.push(...params2);
|
|
226
|
+
return sql + ' = ?';
|
|
227
|
+
}
|
|
228
|
+
params.push(...bindings);
|
|
229
|
+
return sql;
|
|
230
|
+
}
|
|
231
|
+
const sql = this.mapper(key);
|
|
232
|
+
if (value !== null) {
|
|
233
|
+
params.push(value);
|
|
234
|
+
}
|
|
235
|
+
return `${this.knex.ref(sql)} ${replacement} ${value === null ? 'null' : '?'}`;
|
|
236
|
+
}
|
|
237
|
+
wrapQueryGroup(parts, operator = '$and') {
|
|
238
|
+
if (parts.length === 1) {
|
|
239
|
+
return parts[0];
|
|
240
|
+
}
|
|
241
|
+
return `(${parts.join(` ${core_1.GroupOperator[operator]} `)})`;
|
|
242
|
+
}
|
|
181
243
|
mapJoinColumns(type, join) {
|
|
182
244
|
if (join.prop && join.prop.reference === core_1.ReferenceType.ONE_TO_ONE && !join.prop.owner) {
|
|
183
245
|
return join.prop.fieldNames.map((fieldName, idx) => {
|
|
@@ -205,6 +267,9 @@ class QueryBuilderHelper {
|
|
|
205
267
|
if (!(re instanceof RegExp)) {
|
|
206
268
|
return false;
|
|
207
269
|
}
|
|
270
|
+
if (re.flags.includes('i')) {
|
|
271
|
+
return false;
|
|
272
|
+
}
|
|
208
273
|
// when including the opening bracket/paren we consider it complex
|
|
209
274
|
return !re.source.match(/[{[(]/);
|
|
210
275
|
}
|
|
@@ -280,7 +345,7 @@ class QueryBuilderHelper {
|
|
|
280
345
|
}
|
|
281
346
|
qb[m](this.mapper(key, type, cond[key], null), op, cond[key]);
|
|
282
347
|
}
|
|
283
|
-
processCustomExpression(clause, m, key, cond, type
|
|
348
|
+
processCustomExpression(clause, m, key, cond, type) {
|
|
284
349
|
// unwind parameters when ? found in field name
|
|
285
350
|
const count = key.concat('?').match(/\?/g).length - 1;
|
|
286
351
|
const value = core_1.Utils.asArray(cond[key]);
|
|
@@ -301,7 +366,7 @@ class QueryBuilderHelper {
|
|
|
301
366
|
return subCondition.forEach(sub => this.appendQueryCondition(type, sub, qb, '$and', method));
|
|
302
367
|
}
|
|
303
368
|
if (value instanceof RegExp) {
|
|
304
|
-
value =
|
|
369
|
+
value = this.platform.getRegExpValue(value);
|
|
305
370
|
}
|
|
306
371
|
// operators
|
|
307
372
|
const op = Object.keys(core_1.QueryOperator).find(op => op in value);
|
|
@@ -311,68 +376,41 @@ class QueryBuilderHelper {
|
|
|
311
376
|
const replacement = this.getOperatorReplacement(op, value);
|
|
312
377
|
const fields = core_1.Utils.splitPrimaryKeys(key);
|
|
313
378
|
if (fields.length > 1 && Array.isArray(value[op]) && !value[op].every((v) => Array.isArray(v))) {
|
|
314
|
-
|
|
315
|
-
value[op] = this.knex.raw(`${values}(${fields.map(() => '?').join(', ')})`, value[op]);
|
|
379
|
+
value[op] = this.knex.raw(`(${fields.map(() => '?').join(', ')})`, value[op]);
|
|
316
380
|
}
|
|
317
381
|
if (this.subQueries[key]) {
|
|
318
382
|
return void qb[m](this.knex.raw(`(${this.subQueries[key]})`), replacement, value[op]);
|
|
319
383
|
}
|
|
320
|
-
|
|
384
|
+
if (op === '$fulltext') {
|
|
385
|
+
const [a, f] = this.splitField(key);
|
|
386
|
+
const prop = this.getProperty(f, a);
|
|
387
|
+
/* istanbul ignore next */
|
|
388
|
+
if (!prop) {
|
|
389
|
+
throw new Error(`Cannot use $fulltext operator on ${key}, property not found`);
|
|
390
|
+
}
|
|
391
|
+
qb[m](this.knex.raw(this.platform.getFullTextWhereClause(prop), {
|
|
392
|
+
column: this.mapper(key, type, undefined, null),
|
|
393
|
+
query: value[op],
|
|
394
|
+
}));
|
|
395
|
+
}
|
|
396
|
+
else {
|
|
397
|
+
qb[m](this.mapper(key, type, undefined, null), replacement, value[op]);
|
|
398
|
+
}
|
|
321
399
|
}
|
|
322
400
|
getOperatorReplacement(op, value) {
|
|
323
401
|
let replacement = core_1.QueryOperator[op];
|
|
402
|
+
if (op === '$exists') {
|
|
403
|
+
replacement = value[op] ? 'is not' : 'is';
|
|
404
|
+
value[op] = null;
|
|
405
|
+
}
|
|
324
406
|
if (value[op] === null && ['$eq', '$ne'].includes(op)) {
|
|
325
407
|
replacement = op === '$eq' ? 'is' : 'is not';
|
|
326
408
|
}
|
|
327
409
|
if (op === '$re') {
|
|
328
|
-
replacement = this.platform.getRegExpOperator();
|
|
410
|
+
replacement = this.platform.getRegExpOperator(value[op], value.$flags);
|
|
329
411
|
}
|
|
330
412
|
return replacement;
|
|
331
413
|
}
|
|
332
|
-
appendJoinClause(clause, cond, operator) {
|
|
333
|
-
Object.keys(cond).forEach(k => {
|
|
334
|
-
if (k === '$and' || k === '$or') {
|
|
335
|
-
const method = operator === '$or' ? 'orOn' : 'andOn';
|
|
336
|
-
const m = k === '$or' ? 'orOn' : 'andOn';
|
|
337
|
-
return clause[method](outer => cond[k].forEach((sub) => {
|
|
338
|
-
if (core_1.Utils.getObjectKeysSize(sub) === 1) {
|
|
339
|
-
return this.appendJoinClause(outer, sub, k);
|
|
340
|
-
}
|
|
341
|
-
outer[m](inner => this.appendJoinClause(inner, sub, '$and'));
|
|
342
|
-
}));
|
|
343
|
-
}
|
|
344
|
-
this.appendJoinSubClause(clause, cond, k, operator);
|
|
345
|
-
});
|
|
346
|
-
}
|
|
347
|
-
appendJoinSubClause(clause, cond, key, operator) {
|
|
348
|
-
const m = operator === '$or' ? 'orOn' : 'andOn';
|
|
349
|
-
if (cond[key] instanceof RegExp) {
|
|
350
|
-
return void clause[m](this.mapper(key), 'like', this.knex.raw('?', this.getRegExpParam(cond[key])));
|
|
351
|
-
}
|
|
352
|
-
if (core_1.Utils.isPlainObject(cond[key])) {
|
|
353
|
-
return this.processObjectSubClause(cond, key, clause, m);
|
|
354
|
-
}
|
|
355
|
-
if (QueryBuilderHelper.isCustomExpression(key)) {
|
|
356
|
-
return this.processCustomExpression(clause, m, key, cond);
|
|
357
|
-
}
|
|
358
|
-
const op = cond[key] === null ? 'is' : '=';
|
|
359
|
-
clause[m](this.knex.raw(`${this.knex.ref(this.mapper(key, enums_1.QueryType.SELECT, cond[key]))} ${op} ?`, cond[key]));
|
|
360
|
-
}
|
|
361
|
-
processObjectSubClause(cond, key, clause, m) {
|
|
362
|
-
// grouped condition for one field
|
|
363
|
-
if (core_1.Utils.getObjectKeysSize(cond[key]) > 1) {
|
|
364
|
-
const subCondition = Object.entries(cond[key]).map(([subKey, subValue]) => ({ [key]: { [subKey]: subValue } }));
|
|
365
|
-
return void clause[m](inner => subCondition.map(sub => this.appendJoinClause(inner, sub, '$and')));
|
|
366
|
-
}
|
|
367
|
-
// operators
|
|
368
|
-
for (const [op, replacement] of Object.entries(core_1.QueryOperator)) {
|
|
369
|
-
if (!(op in cond[key])) {
|
|
370
|
-
continue;
|
|
371
|
-
}
|
|
372
|
-
clause[m](this.mapper(key), replacement, this.knex.raw('?', cond[key][op]));
|
|
373
|
-
break;
|
|
374
|
-
}
|
|
375
|
-
}
|
|
376
414
|
getQueryOrder(type, orderBy, populate) {
|
|
377
415
|
if (Array.isArray(orderBy)) {
|
|
378
416
|
return orderBy
|
|
@@ -392,12 +430,12 @@ class QueryBuilderHelper {
|
|
|
392
430
|
return;
|
|
393
431
|
}
|
|
394
432
|
// eslint-disable-next-line prefer-const
|
|
395
|
-
let [alias, field] = this.splitField(k);
|
|
433
|
+
let [alias, field] = this.splitField(k, true);
|
|
396
434
|
alias = populate[alias] || alias;
|
|
397
435
|
core_1.Utils.splitPrimaryKeys(field).forEach(f => {
|
|
398
436
|
const prop = this.getProperty(f, alias);
|
|
399
|
-
const noPrefix = (prop && prop.persist === false) || QueryBuilderHelper.isCustomExpression(f);
|
|
400
|
-
const column = this.mapper(noPrefix ? f : `${alias}.${f}`, type);
|
|
437
|
+
const noPrefix = (prop && prop.persist === false && !prop.formula) || QueryBuilderHelper.isCustomExpression(f);
|
|
438
|
+
const column = this.mapper(noPrefix ? f : `${alias}.${f}`, type, undefined, null);
|
|
401
439
|
/* istanbul ignore next */
|
|
402
440
|
const rawColumn = core_1.Utils.isString(column) ? column.split('.').map(e => this.knex.ref(e)).join('.') : column;
|
|
403
441
|
const customOrder = prop?.customOrder;
|
|
@@ -412,14 +450,22 @@ class QueryBuilderHelper {
|
|
|
412
450
|
finalize(type, qb, meta) {
|
|
413
451
|
const useReturningStatement = type === enums_1.QueryType.INSERT && this.platform.usesReturningStatement() && meta && !meta.compositePK;
|
|
414
452
|
if (useReturningStatement) {
|
|
415
|
-
const returningProps = meta.
|
|
453
|
+
const returningProps = meta.hydrateProps.filter(prop => prop.persist !== false && (prop.primary || prop.defaultRaw));
|
|
416
454
|
qb.returning(core_1.Utils.flatten(returningProps.map(prop => prop.fieldNames)));
|
|
417
455
|
}
|
|
418
456
|
}
|
|
419
|
-
splitField(field) {
|
|
457
|
+
splitField(field, greedyAlias = false) {
|
|
420
458
|
const parts = field.split('.');
|
|
421
|
-
|
|
422
|
-
|
|
459
|
+
if (parts.length === 1) {
|
|
460
|
+
return [this.alias, parts[0]];
|
|
461
|
+
}
|
|
462
|
+
if (greedyAlias) {
|
|
463
|
+
const fromField = parts.pop();
|
|
464
|
+
const fromAlias = parts.join('.');
|
|
465
|
+
return [fromAlias, fromField];
|
|
466
|
+
}
|
|
467
|
+
const fromAlias = parts.shift();
|
|
468
|
+
const fromField = parts.join('.');
|
|
423
469
|
return [fromAlias, fromField];
|
|
424
470
|
}
|
|
425
471
|
getLockSQL(qb, lockMode, lockTables = []) {
|
|
@@ -438,7 +484,7 @@ class QueryBuilderHelper {
|
|
|
438
484
|
}
|
|
439
485
|
updateVersionProperty(qb, data) {
|
|
440
486
|
const meta = this.metadata.find(this.entityName);
|
|
441
|
-
if (!meta
|
|
487
|
+
if (!meta?.versionProperty || meta.versionProperty in data) {
|
|
442
488
|
return;
|
|
443
489
|
}
|
|
444
490
|
const versionProperty = meta.properties[meta.versionProperty];
|
|
@@ -464,7 +510,8 @@ class QueryBuilderHelper {
|
|
|
464
510
|
ret = alias + fieldName;
|
|
465
511
|
}
|
|
466
512
|
else {
|
|
467
|
-
const [a,
|
|
513
|
+
const [a, ...rest] = field.split('.');
|
|
514
|
+
const f = rest.join('.');
|
|
468
515
|
ret = a + '.' + this.fieldName(f, a);
|
|
469
516
|
}
|
|
470
517
|
if (quote) {
|
|
@@ -509,15 +556,66 @@ class QueryBuilderHelper {
|
|
|
509
556
|
return prop.fieldNameRaw;
|
|
510
557
|
}
|
|
511
558
|
/* istanbul ignore next */
|
|
512
|
-
return prop.fieldNames[0] ?? field;
|
|
559
|
+
return prop.fieldNames?.[0] ?? field;
|
|
513
560
|
}
|
|
514
561
|
getProperty(field, alias) {
|
|
515
|
-
const entityName = this.aliasMap[alias] || this.entityName;
|
|
562
|
+
const entityName = this.aliasMap[alias]?.entityName || this.entityName;
|
|
516
563
|
const meta = this.metadata.find(entityName);
|
|
517
|
-
|
|
564
|
+
// check if `alias` is not matching an embedded property name instead of alias, e.g. `address.city`
|
|
565
|
+
if (alias && meta) {
|
|
566
|
+
const prop = meta.properties[alias];
|
|
567
|
+
if (prop?.reference === core_1.ReferenceType.EMBEDDED) {
|
|
568
|
+
// we want to select the full object property so hydration works as expected
|
|
569
|
+
if (prop.object) {
|
|
570
|
+
return prop;
|
|
571
|
+
}
|
|
572
|
+
const parts = field.split('.');
|
|
573
|
+
const nest = (p) => parts.length > 0 ? nest(p.embeddedProps[parts.shift()]) : p;
|
|
574
|
+
return nest(prop);
|
|
575
|
+
}
|
|
576
|
+
}
|
|
577
|
+
return meta?.properties[field];
|
|
518
578
|
}
|
|
519
579
|
isTableNameAliasRequired(type) {
|
|
520
|
-
return [enums_1.QueryType.SELECT, enums_1.QueryType.COUNT].includes(type);
|
|
580
|
+
return [enums_1.QueryType.SELECT, enums_1.QueryType.COUNT].includes(type ?? enums_1.QueryType.SELECT);
|
|
581
|
+
}
|
|
582
|
+
mapData(data, properties, convertCustomTypes) {
|
|
583
|
+
if (!properties) {
|
|
584
|
+
return data;
|
|
585
|
+
}
|
|
586
|
+
data = Object.assign({}, data); // copy first
|
|
587
|
+
Object.keys(data).forEach(k => {
|
|
588
|
+
const prop = properties[k];
|
|
589
|
+
if (!prop) {
|
|
590
|
+
return;
|
|
591
|
+
}
|
|
592
|
+
if (prop.embeddedProps && !prop.object) {
|
|
593
|
+
const copy = data[k];
|
|
594
|
+
delete data[k];
|
|
595
|
+
Object.assign(data, this.mapData(copy, prop.embeddedProps, convertCustomTypes));
|
|
596
|
+
return;
|
|
597
|
+
}
|
|
598
|
+
if (prop.joinColumns && Array.isArray(data[k])) {
|
|
599
|
+
const copy = data[k];
|
|
600
|
+
delete data[k];
|
|
601
|
+
prop.joinColumns.forEach((joinColumn, idx) => data[joinColumn] = copy[idx]);
|
|
602
|
+
return;
|
|
603
|
+
}
|
|
604
|
+
if (prop.customType && convertCustomTypes && !this.platform.isRaw(data[k])) {
|
|
605
|
+
data[k] = prop.customType.convertToDatabaseValue(data[k], this.platform, { fromQuery: true, key: k, mode: 'query' });
|
|
606
|
+
}
|
|
607
|
+
if (prop.customType && 'convertToDatabaseValueSQL' in prop.customType && !this.platform.isRaw(data[k])) {
|
|
608
|
+
const quoted = this.platform.quoteValue(data[k]);
|
|
609
|
+
data[k] = this.knex.raw(prop.customType.convertToDatabaseValueSQL(quoted, this.platform).replace(/\?/, '\\?'));
|
|
610
|
+
}
|
|
611
|
+
if (!prop.customType && (Array.isArray(data[k]) || core_1.Utils.isPlainObject(data[k]))) {
|
|
612
|
+
data[k] = JSON.stringify(data[k]);
|
|
613
|
+
}
|
|
614
|
+
if (prop.fieldNames) {
|
|
615
|
+
core_1.Utils.renameKey(data, k, prop.fieldNames[0]);
|
|
616
|
+
}
|
|
617
|
+
});
|
|
618
|
+
return data;
|
|
521
619
|
}
|
|
522
620
|
}
|
|
523
621
|
exports.QueryBuilderHelper = QueryBuilderHelper;
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
@@ -1,7 +1,11 @@
|
|
|
1
1
|
"use strict";
|
|
2
2
|
var __createBinding = (this && this.__createBinding) || (Object.create ? (function(o, m, k, k2) {
|
|
3
3
|
if (k2 === undefined) k2 = k;
|
|
4
|
-
Object.
|
|
4
|
+
var desc = Object.getOwnPropertyDescriptor(m, k);
|
|
5
|
+
if (!desc || ("get" in desc ? !m.__esModule : desc.writable || desc.configurable)) {
|
|
6
|
+
desc = { enumerable: true, get: function() { return m[k]; } };
|
|
7
|
+
}
|
|
8
|
+
Object.defineProperty(o, k2, desc);
|
|
5
9
|
}) : (function(o, m, k, k2) {
|
|
6
10
|
if (k2 === undefined) k2 = k;
|
|
7
11
|
o[k2] = m[k];
|
|
@@ -11,7 +11,7 @@ export declare class DatabaseSchema {
|
|
|
11
11
|
private tables;
|
|
12
12
|
private namespaces;
|
|
13
13
|
constructor(platform: AbstractSqlPlatform, name: string);
|
|
14
|
-
addTable(name: string, schema: string | undefined | null): DatabaseTable;
|
|
14
|
+
addTable(name: string, schema: string | undefined | null, comment?: string): DatabaseTable;
|
|
15
15
|
getTables(): DatabaseTable[];
|
|
16
16
|
getTable(name: string): DatabaseTable | undefined;
|
|
17
17
|
hasTable(name: string): boolean;
|
|
@@ -13,9 +13,10 @@ class DatabaseSchema {
|
|
|
13
13
|
this.tables = [];
|
|
14
14
|
this.namespaces = new Set();
|
|
15
15
|
}
|
|
16
|
-
addTable(name, schema) {
|
|
16
|
+
addTable(name, schema, comment) {
|
|
17
17
|
const namespaceName = schema ?? this.name;
|
|
18
18
|
const table = new DatabaseTable_1.DatabaseTable(this.platform, name, namespaceName);
|
|
19
|
+
table.comment = comment;
|
|
19
20
|
this.tables.push(table);
|
|
20
21
|
if (namespaceName != null) {
|
|
21
22
|
this.namespaces.add(namespaceName);
|
|
@@ -39,21 +40,12 @@ class DatabaseSchema {
|
|
|
39
40
|
}
|
|
40
41
|
static async create(connection, platform, config, schemaName) {
|
|
41
42
|
const schema = new DatabaseSchema(platform, schemaName ?? config.get('schema'));
|
|
42
|
-
const
|
|
43
|
-
|
|
44
|
-
|
|
45
|
-
|
|
46
|
-
|
|
47
|
-
|
|
48
|
-
table.comment = t.table_comment;
|
|
49
|
-
const cols = await platform.getSchemaHelper().getColumns(connection, table.name, table.schema);
|
|
50
|
-
const indexes = await platform.getSchemaHelper().getIndexes(connection, table.name, table.schema);
|
|
51
|
-
const checks = await platform.getSchemaHelper().getChecks(connection, table.name, table.schema);
|
|
52
|
-
const pks = await platform.getSchemaHelper().getPrimaryKeys(connection, indexes, table.name, table.schema);
|
|
53
|
-
const fks = await platform.getSchemaHelper().getForeignKeys(connection, table.name, table.schema);
|
|
54
|
-
const enums = await platform.getSchemaHelper().getEnumDefinitions(connection, checks, table.name, table.schema);
|
|
55
|
-
table.init(cols, indexes, checks, pks, fks, enums);
|
|
56
|
-
}
|
|
43
|
+
const allTables = await connection.execute(platform.getSchemaHelper().getListTablesSQL());
|
|
44
|
+
const parts = config.get('migrations').tableName.split('.');
|
|
45
|
+
const migrationsTableName = parts[1] ?? parts[0];
|
|
46
|
+
const migrationsSchemaName = parts.length > 1 ? parts[0] : config.get('schema', platform.getDefaultSchemaName());
|
|
47
|
+
const tables = allTables.filter(t => t.table_name !== migrationsTableName || (t.schema_name && t.schema_name !== migrationsSchemaName));
|
|
48
|
+
await platform.getSchemaHelper().loadInformationSchema(schema, connection, tables);
|
|
57
49
|
return schema;
|
|
58
50
|
}
|
|
59
51
|
static fromMetadata(metadata, platform, config, schemaName) {
|