@mikro-orm/sql 7.2.0-dev.2 → 7.2.0-dev.20
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/AbstractSqlConnection.d.ts +30 -3
- package/AbstractSqlConnection.js +75 -15
- package/AbstractSqlDriver.d.ts +1 -8
- package/AbstractSqlDriver.js +124 -55
- package/AbstractSqlPlatform.d.ts +4 -2
- package/AbstractSqlPlatform.js +35 -1
- package/SqlEntityManager.d.ts +2 -2
- package/SqlEntityManager.js +5 -4
- package/dialects/mssql/MsSqlNativeQueryBuilder.js +1 -1
- package/dialects/mysql/BaseMySqlPlatform.d.ts +2 -0
- package/dialects/mysql/BaseMySqlPlatform.js +4 -0
- package/dialects/mysql/MySqlSchemaHelper.d.ts +1 -0
- package/dialects/mysql/MySqlSchemaHelper.js +4 -1
- package/dialects/oracledb/OracleNativeQueryBuilder.js +1 -1
- package/dialects/postgresql/BasePostgreSqlPlatform.d.ts +3 -1
- package/dialects/postgresql/BasePostgreSqlPlatform.js +35 -2
- package/dialects/postgresql/PostgreSqlExceptionConverter.js +8 -1
- package/dialects/postgresql/PostgreSqlSchemaHelper.d.ts +22 -1
- package/dialects/postgresql/PostgreSqlSchemaHelper.js +181 -4
- package/dialects/sqlite/BaseSqliteConnection.d.ts +3 -0
- package/dialects/sqlite/BaseSqliteConnection.js +15 -5
- package/dialects/sqlite/SqlitePlatform.d.ts +2 -0
- package/dialects/sqlite/SqlitePlatform.js +4 -0
- package/dialects/sqlite/SqliteSchemaHelper.js +2 -2
- package/package.json +4 -4
- package/plugin/transformer.d.ts +7 -1
- package/plugin/transformer.js +60 -1
- package/query/CriteriaNodeFactory.js +4 -0
- package/query/NativeQueryBuilder.js +1 -1
- package/query/ObjectCriteriaNode.d.ts +1 -0
- package/query/ObjectCriteriaNode.js +30 -5
- package/query/QueryBuilder.d.ts +40 -7
- package/query/QueryBuilder.js +181 -37
- package/query/QueryBuilderHelper.d.ts +5 -0
- package/query/QueryBuilderHelper.js +39 -9
- package/schema/DatabaseSchema.d.ts +4 -0
- package/schema/DatabaseSchema.js +107 -1
- package/schema/DatabaseTable.d.ts +15 -1
- package/schema/DatabaseTable.js +113 -19
- package/schema/SchemaComparator.d.ts +3 -0
- package/schema/SchemaComparator.js +123 -10
- package/schema/SchemaHelper.d.ts +26 -1
- package/schema/SchemaHelper.js +67 -9
- package/schema/SqlSchemaGenerator.d.ts +4 -0
- package/schema/SqlSchemaGenerator.js +59 -22
- package/typings.d.ts +20 -2
|
@@ -6,6 +6,8 @@ import { SqliteExceptionConverter } from './SqliteExceptionConverter.js';
|
|
|
6
6
|
export declare class SqlitePlatform extends AbstractSqlPlatform {
|
|
7
7
|
protected readonly schemaHelper: SqliteSchemaHelper;
|
|
8
8
|
protected readonly exceptionConverter: SqliteExceptionConverter;
|
|
9
|
+
/** sqlite treats null as the lowest value when no placement is requested. */
|
|
10
|
+
sortsNullsLowest(): boolean;
|
|
9
11
|
/** @internal */
|
|
10
12
|
createNativeQueryBuilder(): SqliteNativeQueryBuilder;
|
|
11
13
|
usesDefaultKeyword(): boolean;
|
|
@@ -5,6 +5,10 @@ import { SqliteExceptionConverter } from './SqliteExceptionConverter.js';
|
|
|
5
5
|
export class SqlitePlatform extends AbstractSqlPlatform {
|
|
6
6
|
schemaHelper = new SqliteSchemaHelper(this);
|
|
7
7
|
exceptionConverter = new SqliteExceptionConverter();
|
|
8
|
+
/** sqlite treats null as the lowest value when no placement is requested. */
|
|
9
|
+
sortsNullsLowest() {
|
|
10
|
+
return true;
|
|
11
|
+
}
|
|
8
12
|
/** @internal */
|
|
9
13
|
createNativeQueryBuilder() {
|
|
10
14
|
return new SqliteNativeQueryBuilder(this);
|
|
@@ -489,7 +489,7 @@ export class SqliteSchemaHelper extends SchemaHelper {
|
|
|
489
489
|
* Foreign key references can only point to tables in the same database.
|
|
490
490
|
*/
|
|
491
491
|
getReferencedTableName(referencedTableName, schema) {
|
|
492
|
-
const [
|
|
492
|
+
const [, tableName] = this.splitTableName(referencedTableName);
|
|
493
493
|
// Strip any schema prefix - SQLite REFERENCES clause doesn't support it
|
|
494
494
|
return tableName;
|
|
495
495
|
}
|
|
@@ -561,7 +561,7 @@ export class SqliteSchemaHelper extends SchemaHelper {
|
|
|
561
561
|
for (const event of trigger.events) {
|
|
562
562
|
const name = trigger.events.length > 1 ? `${trigger.name}_${event}` : trigger.name;
|
|
563
563
|
const when = trigger.when ? `\n when ${trigger.when}` : '';
|
|
564
|
-
ret.push(`create trigger ${this.quote(name)} ${timing} ${event.toUpperCase()} on ${table.getQuotedName()} for each ${forEach}${when} begin ${trigger.body}
|
|
564
|
+
ret.push(`create trigger ${this.quote(name)} ${timing} ${event.toUpperCase()} on ${table.getQuotedName()} for each ${forEach}${when} begin ${this.normalizeTriggerBody(trigger.body)} end`);
|
|
565
565
|
}
|
|
566
566
|
return ret.join(';\n');
|
|
567
567
|
}
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@mikro-orm/sql",
|
|
3
|
-
"version": "7.2.0-dev.
|
|
3
|
+
"version": "7.2.0-dev.20",
|
|
4
4
|
"description": "TypeScript ORM for Node.js based on Data Mapper, Unit of Work and Identity Map patterns. Supports MongoDB, MySQL, PostgreSQL and SQLite databases as well as usage with vanilla JavaScript.",
|
|
5
5
|
"keywords": [
|
|
6
6
|
"data-mapper",
|
|
@@ -47,13 +47,13 @@
|
|
|
47
47
|
"copy": "node ../../scripts/copy.mjs"
|
|
48
48
|
},
|
|
49
49
|
"dependencies": {
|
|
50
|
-
"kysely": "0.29.
|
|
50
|
+
"kysely": "0.29.5"
|
|
51
51
|
},
|
|
52
52
|
"devDependencies": {
|
|
53
|
-
"@mikro-orm/core": "^7.1.
|
|
53
|
+
"@mikro-orm/core": "^7.1.15"
|
|
54
54
|
},
|
|
55
55
|
"peerDependencies": {
|
|
56
|
-
"@mikro-orm/core": "7.2.0-dev.
|
|
56
|
+
"@mikro-orm/core": "7.2.0-dev.20"
|
|
57
57
|
},
|
|
58
58
|
"engines": {
|
|
59
59
|
"node": ">= 22.17.0"
|
package/plugin/transformer.d.ts
CHANGED
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
import { type EntityMetadata, type EntityProperty, type Type } from '@mikro-orm/core';
|
|
2
|
-
import { type CommonTableExpressionNameNode, type DeleteQueryNode, type InsertQueryNode, type JoinNode, type MergeQueryNode, type OperationNode, type QueryId, type SelectQueryNode, type UpdateQueryNode, type WithNode, ColumnNode, IdentifierNode, OperationNodeTransformer, SelectionNode, TableNode, ValueNode } from 'kysely';
|
|
2
|
+
import { type BinaryOperationNode, type CommonTableExpressionNameNode, type DeleteQueryNode, type InsertQueryNode, type JoinNode, type MergeQueryNode, type OperationNode, type QueryId, type SelectQueryNode, type UpdateQueryNode, type WithNode, ColumnNode, IdentifierNode, OperationNodeTransformer, SelectionNode, TableNode, ValueNode } from 'kysely';
|
|
3
3
|
import type { MikroKyselyPluginOptions } from './index.js';
|
|
4
4
|
import type { SqlEntityManager } from '../SqlEntityManager.js';
|
|
5
5
|
export declare class MikroTransformer extends OperationNodeTransformer {
|
|
@@ -28,6 +28,12 @@ export declare class MikroTransformer extends OperationNodeTransformer {
|
|
|
28
28
|
processOnUpdateHooks(node: UpdateQueryNode, meta: EntityMetadata): UpdateQueryNode;
|
|
29
29
|
processInsertValues(node: InsertQueryNode, meta: EntityMetadata): InsertQueryNode;
|
|
30
30
|
processUpdateValues(node: UpdateQueryNode, meta: EntityMetadata): UpdateQueryNode;
|
|
31
|
+
transformBinaryOperation(node: BinaryOperationNode, queryId: QueryId): BinaryOperationNode;
|
|
32
|
+
/** Resolve the entity property a comparison's left operand refers to, so its value operand can be converted. */
|
|
33
|
+
resolveOperandProperty(operand: OperationNode): {
|
|
34
|
+
prop: EntityProperty;
|
|
35
|
+
fieldName: string;
|
|
36
|
+
} | undefined;
|
|
31
37
|
processInputValueNode(prop: EntityProperty | undefined, fieldName: string | undefined, valueNode: ValueNode): OperationNode;
|
|
32
38
|
expandSelections(selections: readonly SelectionNode[]): readonly SelectionNode[];
|
|
33
39
|
expandSelection(sel: SelectionNode): SelectionNode[] | null;
|
package/plugin/transformer.js
CHANGED
|
@@ -455,6 +455,60 @@ export class MikroTransformer extends OperationNodeTransformer {
|
|
|
455
455
|
updates,
|
|
456
456
|
};
|
|
457
457
|
}
|
|
458
|
+
transformBinaryOperation(node, queryId) {
|
|
459
|
+
const transformed = super.transformBinaryOperation(node, queryId);
|
|
460
|
+
if (!this.#options.convertValues) {
|
|
461
|
+
return transformed;
|
|
462
|
+
}
|
|
463
|
+
const resolved = this.resolveOperandProperty(transformed.leftOperand);
|
|
464
|
+
if (!resolved) {
|
|
465
|
+
return transformed;
|
|
466
|
+
}
|
|
467
|
+
const { prop, fieldName } = resolved;
|
|
468
|
+
const right = transformed.rightOperand;
|
|
469
|
+
if (ValueNode.is(right)) {
|
|
470
|
+
const converted = this.processInputValueNode(prop, fieldName, right);
|
|
471
|
+
return converted === right ? transformed : { ...transformed, rightOperand: converted };
|
|
472
|
+
}
|
|
473
|
+
if (PrimitiveValueListNode.is(right)) {
|
|
474
|
+
// upgrade to ValueListNode when the type needs SQL-side wrapping, since
|
|
475
|
+
// PrimitiveValueListNode can only hold primitives
|
|
476
|
+
if (prop.hasConvertToDatabaseValueSQL) {
|
|
477
|
+
const values = right.values.map(value => this.processInputValueNode(prop, fieldName, ValueNode.create(value)));
|
|
478
|
+
return { ...transformed, rightOperand: ValueListNode.create(values) };
|
|
479
|
+
}
|
|
480
|
+
const values = right.values.map(value => this.prepareInputValue(prop, value, true));
|
|
481
|
+
return values.every((value, idx) => value === right.values[idx])
|
|
482
|
+
? transformed
|
|
483
|
+
: { ...transformed, rightOperand: PrimitiveValueListNode.create(values) };
|
|
484
|
+
}
|
|
485
|
+
if (ValueListNode.is(right)) {
|
|
486
|
+
let changed = false;
|
|
487
|
+
const values = right.values.map(valueNode => {
|
|
488
|
+
if (!ValueNode.is(valueNode)) {
|
|
489
|
+
return valueNode;
|
|
490
|
+
}
|
|
491
|
+
const converted = this.processInputValueNode(prop, fieldName, valueNode);
|
|
492
|
+
if (converted !== valueNode) {
|
|
493
|
+
changed = true;
|
|
494
|
+
}
|
|
495
|
+
return converted;
|
|
496
|
+
});
|
|
497
|
+
return changed ? { ...transformed, rightOperand: ValueListNode.create(values) } : transformed;
|
|
498
|
+
}
|
|
499
|
+
return transformed;
|
|
500
|
+
}
|
|
501
|
+
/** Resolve the entity property a comparison's left operand refers to, so its value operand can be converted. */
|
|
502
|
+
resolveOperandProperty(operand) {
|
|
503
|
+
if (!ReferenceNode.is(operand) || !ColumnNode.is(operand.column)) {
|
|
504
|
+
return undefined;
|
|
505
|
+
}
|
|
506
|
+
const tableName = operand.table ? this.getTableName(operand.table) : undefined;
|
|
507
|
+
const meta = this.findOwnerMeta(tableName);
|
|
508
|
+
const fieldName = this.normalizeColumnName(operand.column.column);
|
|
509
|
+
const prop = this.findProperty(meta, fieldName);
|
|
510
|
+
return prop ? { prop, fieldName } : undefined;
|
|
511
|
+
}
|
|
458
512
|
processInputValueNode(prop, fieldName, valueNode) {
|
|
459
513
|
const converted = this.prepareInputValue(prop, valueNode.value, true);
|
|
460
514
|
const newValueNode = converted === valueNode.value
|
|
@@ -543,8 +597,13 @@ export class MikroTransformer extends OperationNodeTransformer {
|
|
|
543
597
|
if (name) {
|
|
544
598
|
return this.lookupInContextStack(name) ?? this.#subqueryAliasMap.get(name) ?? this.findEntityMetadata(name);
|
|
545
599
|
}
|
|
600
|
+
// the stack can be empty when transforming a raw root node with embedded expressions
|
|
601
|
+
const context = this.#contextStack[this.#contextStack.length - 1];
|
|
602
|
+
if (!context) {
|
|
603
|
+
return undefined;
|
|
604
|
+
}
|
|
546
605
|
let single;
|
|
547
|
-
for (const meta of
|
|
606
|
+
for (const meta of context.values()) {
|
|
548
607
|
if (!meta) {
|
|
549
608
|
continue;
|
|
550
609
|
}
|
|
@@ -57,6 +57,10 @@ export class CriteriaNodeFactory {
|
|
|
57
57
|
if (isNotEmbedded && prop?.customType instanceof JsonType) {
|
|
58
58
|
return this.createScalarNode(metadata, childEntity, val, node, key, validate);
|
|
59
59
|
}
|
|
60
|
+
// operator payloads under an unresolvable alias-prefixed key (e.g. `a.meta`) are opaque values, not entity criteria
|
|
61
|
+
if (!prop && !rawField && Utils.isOperator(key, false) && String(node.key).includes('.')) {
|
|
62
|
+
return this.createScalarNode(metadata, entityName, val, node, key, validate);
|
|
63
|
+
}
|
|
60
64
|
if (prop?.kind === ReferenceKind.SCALAR && val != null && Object.keys(val).some(f => f in GroupOperator)) {
|
|
61
65
|
throw ValidationError.cannotUseGroupOperatorsInsideScalars(entityName, prop.name, payload);
|
|
62
66
|
}
|
|
@@ -308,7 +308,7 @@ export class NativeQueryBuilder {
|
|
|
308
308
|
const fields = this.options.groupBy.map(field => this.quote(field));
|
|
309
309
|
this.parts.push(`group by ${fields.join(', ')}`);
|
|
310
310
|
}
|
|
311
|
-
if (this.options.having) {
|
|
311
|
+
if (this.options.having?.sql.trim()) {
|
|
312
312
|
this.parts.push(`having ${this.options.having.sql}`);
|
|
313
313
|
this.params.push(...this.options.having.params);
|
|
314
314
|
}
|
|
@@ -13,6 +13,7 @@ export declare class ObjectCriteriaNode<T extends object> extends CriteriaNode<T
|
|
|
13
13
|
private inlineArrayChildPayload;
|
|
14
14
|
private inlineChildPayload;
|
|
15
15
|
private inlineCondition;
|
|
16
|
+
private isCollectionOperator;
|
|
16
17
|
private shouldAutoJoin;
|
|
17
18
|
private autoJoin;
|
|
18
19
|
private isPrefixed;
|
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
import { ALIAS_REPLACEMENT, GroupOperator, QueryFlag, raw, RawQueryFragment, ReferenceKind, Utils, } from '@mikro-orm/core';
|
|
2
2
|
import { CriteriaNode } from './CriteriaNode.js';
|
|
3
3
|
import { JoinType, QueryType } from './enums.js';
|
|
4
|
-
const COLLECTION_OPERATORS = ['$some', '$none', '$every', '$size'];
|
|
4
|
+
const COLLECTION_OPERATORS = ['$some', '$none', '$every', '$size', '$all'];
|
|
5
5
|
/**
|
|
6
6
|
* @internal
|
|
7
7
|
*/
|
|
@@ -17,7 +17,7 @@ export class ObjectCriteriaNode extends CriteriaNode {
|
|
|
17
17
|
alias = nestedAlias;
|
|
18
18
|
}
|
|
19
19
|
if (this.shouldAutoJoin(qb, nestedAlias)) {
|
|
20
|
-
if (keys.some(k =>
|
|
20
|
+
if (keys.some(k => this.isCollectionOperator(k))) {
|
|
21
21
|
if (![ReferenceKind.MANY_TO_MANY, ReferenceKind.ONE_TO_MANY].includes(this.prop.kind)) {
|
|
22
22
|
// ignore collection operators when used on a non-relational property - this can happen when they get into
|
|
23
23
|
// populateWhere via `infer` on m:n properties with select-in strategy
|
|
@@ -34,11 +34,24 @@ export class ObjectCriteriaNode extends CriteriaNode {
|
|
|
34
34
|
const primaryKeys = parentMeta.primaryKeys.map(pk => {
|
|
35
35
|
return [QueryType.SELECT, QueryType.COUNT].includes(qb.type) ? `${knownKey ? alias : ownerAlias}.${pk}` : pk;
|
|
36
36
|
});
|
|
37
|
+
const conditions = [];
|
|
38
|
+
let matchNothing = false;
|
|
37
39
|
for (const key of keys) {
|
|
38
40
|
if (typeof key !== 'string' || !COLLECTION_OPERATORS.includes(key)) {
|
|
39
41
|
throw new Error('Mixing collection operators with other filters is not allowed.');
|
|
40
42
|
}
|
|
41
43
|
const payload = this.payload[key].unwrap();
|
|
44
|
+
// `$all` requires every listed item to be present, which is an intersection of `$some` conditions
|
|
45
|
+
if (key === '$all') {
|
|
46
|
+
// an empty `$all` matches nothing, same as in mongo
|
|
47
|
+
matchNothing ||= payload.length === 0;
|
|
48
|
+
conditions.push(...payload.map(item => ['$some', item]));
|
|
49
|
+
}
|
|
50
|
+
else {
|
|
51
|
+
conditions.push([key, payload]);
|
|
52
|
+
}
|
|
53
|
+
}
|
|
54
|
+
for (const [key, payload] of conditions) {
|
|
42
55
|
// entities with a fixed schema must resolve the `from` table's own schema in the subquery,
|
|
43
56
|
// otherwise a nested operator inherits the root entity's schema (GH #7894); for wildcard or
|
|
44
57
|
// schema-less entities the schema is resolved dynamically and needs to be carried over
|
|
@@ -70,6 +83,9 @@ export class ObjectCriteriaNode extends CriteriaNode {
|
|
|
70
83
|
[Utils.getPrimaryKeyHash(primaryKeys)]: { [op]: sub.getNativeQuery().toRaw() },
|
|
71
84
|
});
|
|
72
85
|
}
|
|
86
|
+
if (matchNothing) {
|
|
87
|
+
$and.push({ [Utils.getPrimaryKeyHash(primaryKeys)]: { $in: [] } });
|
|
88
|
+
}
|
|
73
89
|
if ($and.length === 1) {
|
|
74
90
|
return $and[0];
|
|
75
91
|
}
|
|
@@ -113,7 +129,8 @@ export class ObjectCriteriaNode extends CriteriaNode {
|
|
|
113
129
|
}
|
|
114
130
|
else if (isRawField) {
|
|
115
131
|
const rawField = RawQueryFragment.getKnownFragment(field);
|
|
116
|
-
|
|
132
|
+
qb.ensureTPTJoins();
|
|
133
|
+
o[raw(qb.helper.replaceAliases(rawField.sql, alias), rawField.params)] = payload;
|
|
117
134
|
}
|
|
118
135
|
else if (!childNode.validate && !childNode.prop && !field.includes('.') && !operator) {
|
|
119
136
|
// wrap unknown fields in raw() to prevent alias prefixing (e.g. raw SQL aliases in HAVING)
|
|
@@ -153,7 +170,7 @@ export class ObjectCriteriaNode extends CriteriaNode {
|
|
|
153
170
|
alias = nestedAlias;
|
|
154
171
|
}
|
|
155
172
|
if (this.shouldAutoJoin(qb, nestedAlias)) {
|
|
156
|
-
return !keys.some(k =>
|
|
173
|
+
return !keys.some(k => this.isCollectionOperator(k));
|
|
157
174
|
}
|
|
158
175
|
return keys.some(field => {
|
|
159
176
|
const childNode = this.payload[field];
|
|
@@ -237,6 +254,14 @@ export class ObjectCriteriaNode extends CriteriaNode {
|
|
|
237
254
|
delete o[key];
|
|
238
255
|
o.$and = $and;
|
|
239
256
|
}
|
|
257
|
+
isCollectionOperator(key) {
|
|
258
|
+
if (typeof key !== 'string' || !COLLECTION_OPERATORS.includes(key)) {
|
|
259
|
+
return false;
|
|
260
|
+
}
|
|
261
|
+
// `$all` is primarily a mongo array operator, in SQL it is supported only on collections
|
|
262
|
+
return (key !== '$all' ||
|
|
263
|
+
(!!this.prop && [ReferenceKind.MANY_TO_MANY, ReferenceKind.ONE_TO_MANY].includes(this.prop.kind)));
|
|
264
|
+
}
|
|
240
265
|
shouldAutoJoin(qb, nestedAlias) {
|
|
241
266
|
if (!this.prop || !this.parent) {
|
|
242
267
|
return false;
|
|
@@ -245,7 +270,7 @@ export class ObjectCriteriaNode extends CriteriaNode {
|
|
|
245
270
|
if (keys.every(k => typeof k === 'string' && k.includes('.') && k.startsWith(`${qb.alias}.`))) {
|
|
246
271
|
return false;
|
|
247
272
|
}
|
|
248
|
-
if (keys.some(k =>
|
|
273
|
+
if (keys.some(k => this.isCollectionOperator(k))) {
|
|
249
274
|
return true;
|
|
250
275
|
}
|
|
251
276
|
const meta = this.metadata.find(this.entityName);
|
package/query/QueryBuilder.d.ts
CHANGED
|
@@ -152,11 +152,10 @@ type ContextFilterKeys<Context> = {
|
|
|
152
152
|
type RawFilterKeys<RawAliases extends string> = {
|
|
153
153
|
[K in RawAliases]?: AliasedFilterValue;
|
|
154
154
|
};
|
|
155
|
-
type NestedFilterCondition<Entity, RootAlias extends string, Context, RawAliases extends string> = ObjectQuery<Entity> & (IsNever<RootAlias> extends true ? {} : string extends RootAlias ? {} : RootAliasFilterKeys<RootAlias, Entity>) & ([Context] extends [never] ? {} : ContextFilterKeys<Context>) & (IsNever<RawAliases> extends true ? {} : string extends RawAliases ? {} : RawFilterKeys<RawAliases>);
|
|
156
155
|
type GroupOperators<RootAlias extends string, Context, Entity, RawAliases extends string> = {
|
|
157
|
-
$and?:
|
|
158
|
-
$or?:
|
|
159
|
-
$not?:
|
|
156
|
+
$and?: QBFilterQuery<Entity, RootAlias, Context, RawAliases>[];
|
|
157
|
+
$or?: QBFilterQuery<Entity, RootAlias, Context, RawAliases>[];
|
|
158
|
+
$not?: QBFilterQuery<Entity, RootAlias, Context, RawAliases>;
|
|
160
159
|
};
|
|
161
160
|
export type AliasedFilterCondition<RootAlias extends string, Context, Entity, RawAliases extends string = never> = (IsNever<RootAlias> extends true ? {} : string extends RootAlias ? {} : RootAliasFilterKeys<RootAlias, Entity>) & ([Context] extends [never] ? {} : ContextFilterKeys<Context>) & (IsNever<RawAliases> extends true ? {} : string extends RawAliases ? {} : RawFilterKeys<RawAliases>) & GroupOperators<RootAlias, Context, Entity, RawAliases>;
|
|
162
161
|
export type QBFilterQuery<Entity, RootAlias extends string = never, Context = never, RawAliases extends string = never> = FilterObject<Entity> & AliasedFilterCondition<RootAlias, Context, Entity, RawAliases>;
|
|
@@ -440,7 +439,7 @@ export declare class QueryBuilder<Entity extends object = AnyEntity, RootAlias e
|
|
|
440
439
|
*/
|
|
441
440
|
join<Field extends QBField<Entity, RootAlias, Context>, Alias extends string>(field: Field, alias: Alias, cond?: JoinCondition<JoinedEntityType<Entity, Context, Field & string>, Alias>, type?: JoinType, path?: string, schema?: string): SelectQueryBuilder<Entity, RootAlias, ModifyHint<RootAlias, Context, Hint, Field> & {}, ModifyContext<Entity, Context, Field, Alias>, RawAliases, Fields, CTEs>;
|
|
442
441
|
/**
|
|
443
|
-
* Adds a JOIN clause to the query for a subquery.
|
|
442
|
+
* Adds a JOIN clause to the query for a subquery. Use `sql.ref('...')` to join a table or CTE by name.
|
|
444
443
|
*/
|
|
445
444
|
join<Alias extends string>(field: RawQueryFragment | QueryBuilder<any>, alias: Alias, cond?: RawJoinCondition, type?: JoinType, path?: string, schema?: string): SelectQueryBuilder<Entity, RootAlias, Hint, ModifyContext<Entity, Context, string, Alias>, RawAliases, Fields, CTEs>;
|
|
446
445
|
/**
|
|
@@ -448,7 +447,7 @@ export declare class QueryBuilder<Entity extends object = AnyEntity, RootAlias e
|
|
|
448
447
|
*/
|
|
449
448
|
innerJoin<Field extends QBField<Entity, RootAlias, Context>, Alias extends string>(field: Field, alias: Alias, cond?: JoinCondition<JoinedEntityType<Entity, Context, Field & string>, Alias>, schema?: string): SelectQueryBuilder<Entity, RootAlias, ModifyHint<RootAlias, Context, Hint, Field> & {}, ModifyContext<Entity, Context, Field, Alias>, RawAliases, Fields, CTEs>;
|
|
450
449
|
/**
|
|
451
|
-
* Adds an INNER JOIN clause to the query for a subquery.
|
|
450
|
+
* Adds an INNER JOIN clause to the query for a subquery. Use `sql.ref('...')` to join a table or CTE by name.
|
|
452
451
|
*/
|
|
453
452
|
innerJoin<Alias extends string>(field: RawQueryFragment | QueryBuilder<any>, alias: Alias, cond?: RawJoinCondition, schema?: string): SelectQueryBuilder<Entity, RootAlias, Hint, ModifyContext<Entity, Context, string, Alias>, RawAliases, Fields, CTEs>;
|
|
454
453
|
innerJoinLateral<Alias extends string>(field: RawQueryFragment | QueryBuilder<any>, alias: Alias, cond?: RawJoinCondition, schema?: string): SelectQueryBuilder<Entity, RootAlias, Hint, ModifyContext<Entity, Context, string, Alias>, RawAliases, Fields, CTEs>;
|
|
@@ -457,7 +456,7 @@ export declare class QueryBuilder<Entity extends object = AnyEntity, RootAlias e
|
|
|
457
456
|
*/
|
|
458
457
|
leftJoin<Field extends QBField<Entity, RootAlias, Context>, Alias extends string>(field: Field, alias: Alias, cond?: JoinCondition<JoinedEntityType<Entity, Context, Field & string>, Alias>, schema?: string): SelectQueryBuilder<Entity, RootAlias, ModifyHint<RootAlias, Context, Hint, Field> & {}, ModifyContext<Entity, Context, Field, Alias>, RawAliases, Fields, CTEs>;
|
|
459
458
|
/**
|
|
460
|
-
* Adds a LEFT JOIN clause to the query for a subquery.
|
|
459
|
+
* Adds a LEFT JOIN clause to the query for a subquery. Use `sql.ref('...')` to join a table or CTE by name.
|
|
461
460
|
*/
|
|
462
461
|
leftJoin<Alias extends string>(field: RawQueryFragment | QueryBuilder<any>, alias: Alias, cond?: RawJoinCondition, schema?: string): SelectQueryBuilder<Entity, RootAlias, Hint, ModifyContext<Entity, Context, string, Alias>, RawAliases, Fields, CTEs>;
|
|
463
462
|
leftJoinLateral<Alias extends string>(field: RawQueryFragment | QueryBuilder<any>, alias: Alias, cond?: RawJoinCondition, schema?: string): SelectQueryBuilder<Entity, RootAlias, Hint, ModifyContext<Entity, Context, string, Alias>, RawAliases, Fields, CTEs>;
|
|
@@ -506,6 +505,17 @@ export declare class QueryBuilder<Entity extends object = AnyEntity, RootAlias e
|
|
|
506
505
|
* @internal
|
|
507
506
|
*/
|
|
508
507
|
applyJoinedFilters(em: EntityManager, filterOptions: FilterOptions | undefined): Promise<void>;
|
|
508
|
+
/**
|
|
509
|
+
* The `on` clause of `condJoin` — its explicit join condition or a filter condition merged into
|
|
510
|
+
* it — can reference the alias of any join in its subtree, both auto-joins created while
|
|
511
|
+
* processing the condition and pre-existing joined paths, all of which render after `condJoin`
|
|
512
|
+
* and would be forward alias references (issues #7681, #8090, #8099). When that happens, fold the
|
|
513
|
+
* subtree into `condJoin`, so it renders as a single parenthesized join group and every alias
|
|
514
|
+
* shares the scope of the outer `on` clause.
|
|
515
|
+
*/
|
|
516
|
+
private nestReferencedJoins;
|
|
517
|
+
private getJoinSubtree;
|
|
518
|
+
private condReferencesAlias;
|
|
509
519
|
withSubQuery(subQuery: RawQueryFragment | NativeQueryBuilder, alias: string): this;
|
|
510
520
|
/**
|
|
511
521
|
* Adds a WHERE clause to the query using an object condition.
|
|
@@ -772,6 +782,8 @@ export declare class QueryBuilder<Entity extends object = AnyEntity, RootAlias e
|
|
|
772
782
|
* @internal
|
|
773
783
|
*/
|
|
774
784
|
getJoinForPath(path: string, options?: ICriteriaNodeProcessOptions): JoinOptions | undefined;
|
|
785
|
+
/** Branch-insensitive path matching still must not cross branches — segments with different explicit branch markers (e.g. `Mobile[0]` vs `Mobile[1]`) belong to sibling joins. */
|
|
786
|
+
private branchesConflict;
|
|
775
787
|
/**
|
|
776
788
|
* @internal
|
|
777
789
|
*/
|
|
@@ -938,6 +950,13 @@ export declare class QueryBuilder<Entity extends object = AnyEntity, RootAlias e
|
|
|
938
950
|
*/
|
|
939
951
|
addPropertyJoin(prop: EntityProperty, ownerAlias: string, alias: string, type: JoinType, path: string, schema?: string): string;
|
|
940
952
|
private joinReference;
|
|
953
|
+
/**
|
|
954
|
+
* Walks the TPT inheritance chain of `leafMeta` and INNER JOINs each parent table.
|
|
955
|
+
* Registers the parent aliases in `state.tptAlias` so column resolution finds them
|
|
956
|
+
* when conditions reference parent-table columns.
|
|
957
|
+
* @internal
|
|
958
|
+
*/
|
|
959
|
+
addTPTParentJoins(leafMeta: EntityMetadata, leafAlias: string, basePath: string): void;
|
|
941
960
|
protected prepareFields<T>(fields: InternalField<T>[], type?: 'where' | 'groupBy' | 'sub-query', schema?: string): (string | RawQueryFragment)[];
|
|
942
961
|
/**
|
|
943
962
|
* Resolves nested paths like `a.books.title` to their actual field references.
|
|
@@ -984,6 +1003,20 @@ export declare class QueryBuilder<Entity extends object = AnyEntity, RootAlias e
|
|
|
984
1003
|
processPopulateHint(): void;
|
|
985
1004
|
private processPopulateWhere;
|
|
986
1005
|
private mergeOnConditions;
|
|
1006
|
+
/**
|
|
1007
|
+
* `$or` branches can be moved to a join's `on` clause only when they all target that same join
|
|
1008
|
+
* and stay flat — a partial `$or` in the `on` clause would drop rows matching a sibling branch,
|
|
1009
|
+
* and nested operators cannot be preserved inside a distributed disjunction, as `mergeOnConditions`
|
|
1010
|
+
* would flatten them into `and` conjuncts.
|
|
1011
|
+
*/
|
|
1012
|
+
private canDistributeOrBranches;
|
|
1013
|
+
private getOrBranchAliases;
|
|
1014
|
+
/**
|
|
1015
|
+
* An entity filter's `$or` that cannot be distributed is applied intact to the `on` clause of the
|
|
1016
|
+
* outermost join common to all targeted joins, nesting the targeted joins under it so the clause
|
|
1017
|
+
* can reference their aliases.
|
|
1018
|
+
*/
|
|
1019
|
+
private mergeFilterOrCondition;
|
|
987
1020
|
/**
|
|
988
1021
|
* When adding an inner join on a left joined relation, we need to nest them,
|
|
989
1022
|
* otherwise the inner join could discard rows of the root table.
|