@spinajs/orm 2.0.480 → 2.0.482
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +72 -3
- package/lib/cjs/bootstrap.js +10 -0
- package/lib/cjs/bootstrap.js.map +1 -1
- package/lib/cjs/builders.d.ts +115 -11
- package/lib/cjs/builders.d.ts.map +1 -1
- package/lib/cjs/builders.js +342 -283
- package/lib/cjs/builders.js.map +1 -1
- package/lib/cjs/converters.d.ts +9 -2
- package/lib/cjs/converters.d.ts.map +1 -1
- package/lib/cjs/converters.js +48 -8
- package/lib/cjs/converters.js.map +1 -1
- package/lib/cjs/decorators.d.ts +26 -3
- package/lib/cjs/decorators.d.ts.map +1 -1
- package/lib/cjs/decorators.js +95 -42
- package/lib/cjs/decorators.js.map +1 -1
- package/lib/cjs/dehydrators.js +1 -1
- package/lib/cjs/dehydrators.js.map +1 -1
- package/lib/cjs/descriptor.d.ts +1 -0
- package/lib/cjs/descriptor.d.ts.map +1 -1
- package/lib/cjs/descriptor.js +16 -40
- package/lib/cjs/descriptor.js.map +1 -1
- package/lib/cjs/driver.d.ts +136 -7
- package/lib/cjs/driver.d.ts.map +1 -1
- package/lib/cjs/driver.js +279 -0
- package/lib/cjs/driver.js.map +1 -1
- package/lib/cjs/existsRelationHandlers.d.ts +29 -0
- package/lib/cjs/existsRelationHandlers.d.ts.map +1 -0
- package/lib/cjs/existsRelationHandlers.js +104 -0
- package/lib/cjs/existsRelationHandlers.js.map +1 -0
- package/lib/cjs/fp.d.ts.map +1 -1
- package/lib/cjs/fp.js +10 -15
- package/lib/cjs/fp.js.map +1 -1
- package/lib/cjs/hydrators.d.ts.map +1 -1
- package/lib/cjs/hydrators.js +29 -2
- package/lib/cjs/hydrators.js.map +1 -1
- package/lib/cjs/identity-map.d.ts +60 -0
- package/lib/cjs/identity-map.d.ts.map +1 -0
- package/lib/cjs/identity-map.js +135 -0
- package/lib/cjs/identity-map.js.map +1 -0
- package/lib/cjs/index.d.ts +12 -0
- package/lib/cjs/index.d.ts.map +1 -1
- package/lib/cjs/index.js +12 -0
- package/lib/cjs/index.js.map +1 -1
- package/lib/cjs/interfaces.d.ts +295 -9
- package/lib/cjs/interfaces.d.ts.map +1 -1
- package/lib/cjs/interfaces.js +54 -1
- package/lib/cjs/interfaces.js.map +1 -1
- package/lib/cjs/metadata.d.ts.map +1 -1
- package/lib/cjs/metadata.js +4 -1
- package/lib/cjs/metadata.js.map +1 -1
- package/lib/cjs/metrics.d.ts +53 -0
- package/lib/cjs/metrics.d.ts.map +1 -0
- package/lib/cjs/metrics.js +82 -0
- package/lib/cjs/metrics.js.map +1 -0
- package/lib/cjs/middlewares.d.ts +13 -1
- package/lib/cjs/middlewares.d.ts.map +1 -1
- package/lib/cjs/middlewares.js +55 -15
- package/lib/cjs/middlewares.js.map +1 -1
- package/lib/cjs/model.d.ts +129 -13
- package/lib/cjs/model.d.ts.map +1 -1
- package/lib/cjs/model.js +449 -93
- package/lib/cjs/model.js.map +1 -1
- package/lib/cjs/orm.d.ts.map +1 -1
- package/lib/cjs/orm.js +12 -3
- package/lib/cjs/orm.js.map +1 -1
- package/lib/cjs/orphan.d.ts +27 -0
- package/lib/cjs/orphan.d.ts.map +1 -0
- package/lib/cjs/orphan.js +55 -0
- package/lib/cjs/orphan.js.map +1 -0
- package/lib/cjs/primary-keys.d.ts +60 -0
- package/lib/cjs/primary-keys.d.ts.map +1 -0
- package/lib/cjs/primary-keys.js +243 -0
- package/lib/cjs/primary-keys.js.map +1 -0
- package/lib/cjs/relation-objects.d.ts +101 -9
- package/lib/cjs/relation-objects.d.ts.map +1 -1
- package/lib/cjs/relation-objects.js +194 -43
- package/lib/cjs/relation-objects.js.map +1 -1
- package/lib/cjs/relations.d.ts +7 -1
- package/lib/cjs/relations.d.ts.map +1 -1
- package/lib/cjs/relations.js +16 -1
- package/lib/cjs/relations.js.map +1 -1
- package/lib/cjs/resilience.d.ts +50 -0
- package/lib/cjs/resilience.d.ts.map +1 -0
- package/lib/cjs/resilience.js +70 -0
- package/lib/cjs/resilience.js.map +1 -0
- package/lib/cjs/snapshot.d.ts +58 -0
- package/lib/cjs/snapshot.d.ts.map +1 -0
- package/lib/cjs/snapshot.js +125 -0
- package/lib/cjs/snapshot.js.map +1 -0
- package/lib/cjs/statements.d.ts +11 -1
- package/lib/cjs/statements.d.ts.map +1 -1
- package/lib/cjs/statements.js +13 -2
- package/lib/cjs/statements.js.map +1 -1
- package/lib/cjs/subject-builder.d.ts +79 -0
- package/lib/cjs/subject-builder.d.ts.map +1 -0
- package/lib/cjs/subject-builder.js +296 -0
- package/lib/cjs/subject-builder.js.map +1 -0
- package/lib/cjs/subject-executor.d.ts +122 -0
- package/lib/cjs/subject-executor.d.ts.map +1 -0
- package/lib/cjs/subject-executor.js +319 -0
- package/lib/cjs/subject-executor.js.map +1 -0
- package/lib/cjs/subject-sorter.d.ts +70 -0
- package/lib/cjs/subject-sorter.d.ts.map +1 -0
- package/lib/cjs/subject-sorter.js +173 -0
- package/lib/cjs/subject-sorter.js.map +1 -0
- package/lib/cjs/subject.d.ts +100 -0
- package/lib/cjs/subject.d.ts.map +1 -0
- package/lib/cjs/subject.js +89 -0
- package/lib/cjs/subject.js.map +1 -0
- package/lib/cjs/unit-of-work.d.ts +52 -0
- package/lib/cjs/unit-of-work.d.ts.map +1 -0
- package/lib/cjs/unit-of-work.js +170 -0
- package/lib/cjs/unit-of-work.js.map +1 -0
- package/lib/mjs/bootstrap.js +10 -0
- package/lib/mjs/bootstrap.js.map +1 -1
- package/lib/mjs/builders.d.ts +115 -11
- package/lib/mjs/builders.d.ts.map +1 -1
- package/lib/mjs/builders.js +344 -285
- package/lib/mjs/builders.js.map +1 -1
- package/lib/mjs/converters.d.ts +9 -2
- package/lib/mjs/converters.d.ts.map +1 -1
- package/lib/mjs/converters.js +48 -8
- package/lib/mjs/converters.js.map +1 -1
- package/lib/mjs/decorators.d.ts +26 -3
- package/lib/mjs/decorators.d.ts.map +1 -1
- package/lib/mjs/decorators.js +96 -44
- package/lib/mjs/decorators.js.map +1 -1
- package/lib/mjs/dehydrators.js +1 -1
- package/lib/mjs/dehydrators.js.map +1 -1
- package/lib/mjs/descriptor.d.ts +1 -0
- package/lib/mjs/descriptor.d.ts.map +1 -1
- package/lib/mjs/descriptor.js +17 -39
- package/lib/mjs/descriptor.js.map +1 -1
- package/lib/mjs/driver.d.ts +136 -7
- package/lib/mjs/driver.d.ts.map +1 -1
- package/lib/mjs/driver.js +279 -0
- package/lib/mjs/driver.js.map +1 -1
- package/lib/mjs/existsRelationHandlers.d.ts +29 -0
- package/lib/mjs/existsRelationHandlers.d.ts.map +1 -0
- package/lib/mjs/existsRelationHandlers.js +100 -0
- package/lib/mjs/existsRelationHandlers.js.map +1 -0
- package/lib/mjs/fp.d.ts.map +1 -1
- package/lib/mjs/fp.js +10 -15
- package/lib/mjs/fp.js.map +1 -1
- package/lib/mjs/hydrators.d.ts.map +1 -1
- package/lib/mjs/hydrators.js +29 -2
- package/lib/mjs/hydrators.js.map +1 -1
- package/lib/mjs/identity-map.d.ts +60 -0
- package/lib/mjs/identity-map.d.ts.map +1 -0
- package/lib/mjs/identity-map.js +130 -0
- package/lib/mjs/identity-map.js.map +1 -0
- package/lib/mjs/index.d.ts +12 -0
- package/lib/mjs/index.d.ts.map +1 -1
- package/lib/mjs/index.js +12 -0
- package/lib/mjs/index.js.map +1 -1
- package/lib/mjs/interfaces.d.ts +295 -9
- package/lib/mjs/interfaces.d.ts.map +1 -1
- package/lib/mjs/interfaces.js +53 -0
- package/lib/mjs/interfaces.js.map +1 -1
- package/lib/mjs/metadata.d.ts.map +1 -1
- package/lib/mjs/metadata.js +4 -1
- package/lib/mjs/metadata.js.map +1 -1
- package/lib/mjs/metrics.d.ts +53 -0
- package/lib/mjs/metrics.d.ts.map +1 -0
- package/lib/mjs/metrics.js +76 -0
- package/lib/mjs/metrics.js.map +1 -0
- package/lib/mjs/middlewares.d.ts +13 -1
- package/lib/mjs/middlewares.d.ts.map +1 -1
- package/lib/mjs/middlewares.js +55 -15
- package/lib/mjs/middlewares.js.map +1 -1
- package/lib/mjs/model.d.ts +129 -13
- package/lib/mjs/model.d.ts.map +1 -1
- package/lib/mjs/model.js +451 -95
- package/lib/mjs/model.js.map +1 -1
- package/lib/mjs/orm.d.ts.map +1 -1
- package/lib/mjs/orm.js +12 -3
- package/lib/mjs/orm.js.map +1 -1
- package/lib/mjs/orphan.d.ts +27 -0
- package/lib/mjs/orphan.d.ts.map +1 -0
- package/lib/mjs/orphan.js +52 -0
- package/lib/mjs/orphan.js.map +1 -0
- package/lib/mjs/primary-keys.d.ts +60 -0
- package/lib/mjs/primary-keys.d.ts.map +1 -0
- package/lib/mjs/primary-keys.js +223 -0
- package/lib/mjs/primary-keys.js.map +1 -0
- package/lib/mjs/relation-objects.d.ts +101 -9
- package/lib/mjs/relation-objects.d.ts.map +1 -1
- package/lib/mjs/relation-objects.js +194 -43
- package/lib/mjs/relation-objects.js.map +1 -1
- package/lib/mjs/relations.d.ts +7 -1
- package/lib/mjs/relations.d.ts.map +1 -1
- package/lib/mjs/relations.js +16 -1
- package/lib/mjs/relations.js.map +1 -1
- package/lib/mjs/resilience.d.ts +50 -0
- package/lib/mjs/resilience.d.ts.map +1 -0
- package/lib/mjs/resilience.js +64 -0
- package/lib/mjs/resilience.js.map +1 -0
- package/lib/mjs/snapshot.d.ts +58 -0
- package/lib/mjs/snapshot.d.ts.map +1 -0
- package/lib/mjs/snapshot.js +115 -0
- package/lib/mjs/snapshot.js.map +1 -0
- package/lib/mjs/statements.d.ts +11 -1
- package/lib/mjs/statements.d.ts.map +1 -1
- package/lib/mjs/statements.js +14 -3
- package/lib/mjs/statements.js.map +1 -1
- package/lib/mjs/subject-builder.d.ts +79 -0
- package/lib/mjs/subject-builder.d.ts.map +1 -0
- package/lib/mjs/subject-builder.js +292 -0
- package/lib/mjs/subject-builder.js.map +1 -0
- package/lib/mjs/subject-executor.d.ts +122 -0
- package/lib/mjs/subject-executor.d.ts.map +1 -0
- package/lib/mjs/subject-executor.js +312 -0
- package/lib/mjs/subject-executor.js.map +1 -0
- package/lib/mjs/subject-sorter.d.ts +70 -0
- package/lib/mjs/subject-sorter.d.ts.map +1 -0
- package/lib/mjs/subject-sorter.js +168 -0
- package/lib/mjs/subject-sorter.js.map +1 -0
- package/lib/mjs/subject.d.ts +100 -0
- package/lib/mjs/subject.d.ts.map +1 -0
- package/lib/mjs/subject.js +84 -0
- package/lib/mjs/subject.js.map +1 -0
- package/lib/mjs/unit-of-work.d.ts +52 -0
- package/lib/mjs/unit-of-work.d.ts.map +1 -0
- package/lib/mjs/unit-of-work.js +166 -0
- package/lib/mjs/unit-of-work.js.map +1 -0
- package/lib/tsconfig.cjs.tsbuildinfo +1 -1
- package/lib/tsconfig.mjs.tsbuildinfo +1 -1
- package/package.json +9 -7
package/lib/mjs/builders.js
CHANGED
|
@@ -10,7 +10,7 @@ var __metadata = (this && this.__metadata) || function (k, v) {
|
|
|
10
10
|
var RawQuery_1, WhereBuilder_1, SelectQueryBuilder_1;
|
|
11
11
|
/* eslint-disable prettier/prettier */
|
|
12
12
|
import { Container, Inject, NewInstance, DI, Injectable, isConstructor } from '@spinajs/di';
|
|
13
|
-
import { InvalidArgument, MethodNotImplemented, InvalidOperation } from '@spinajs/exceptions';
|
|
13
|
+
import { InvalidArgument, MethodNotImplemented, InvalidOperation, NotSupported } from '@spinajs/exceptions';
|
|
14
14
|
import { OrmException, OrmNotFoundException } from './exceptions.js';
|
|
15
15
|
import _ from 'lodash';
|
|
16
16
|
import { use } from 'typescript-mix';
|
|
@@ -18,9 +18,10 @@ import { ColumnMethods, ColumnType, QueryMethod, SortOrder, WhereBoolean, SqlOpe
|
|
|
18
18
|
import { DeleteQueryCompiler, InsertQueryCompiler, SelectQueryCompiler, TruncateTableQueryCompiler, TableQueryCompiler, AlterTableQueryCompiler, UpdateQueryCompiler, QueryContext, IndexQueryCompiler, RelationType, ReferentialAction, DefaultValueBuilder, ColumnAlterationType, TableExistsCompiler, DropViewCompiler, DropTableCompiler, TableCloneQueryCompiler, QueryMiddleware, DropEventQueryCompiler, EventQueryCompiler, RawSchemaQueryCompiler } from './interfaces.js';
|
|
19
19
|
import { BetweenStatement, ColumnMethodStatement, ColumnStatement, ExistsQueryStatement, InSetStatement, InStatement, RawQueryStatement, WhereQueryStatement, WhereStatement, ColumnRawStatement, JoinStatement, WithRecursiveStatement, GroupByStatement, Wrap, LazyQueryStatement } from './statements.js';
|
|
20
20
|
import { BelongsToRelation, OneToManyRelation, ManyToManyRelation, BelongsToRecursiveRelation, QueryRelation, VirtualRelation } from './relations.js';
|
|
21
|
-
import { Lazy } from '@spinajs/util';
|
|
21
|
+
import { Lazy, isNullOrWhitespace, _check_arg, _positive } from '@spinajs/util';
|
|
22
22
|
import { DiscriminationMapMiddleware } from './discrimination-middleware.js';
|
|
23
23
|
import { extractModelDescriptor } from './descriptor.js';
|
|
24
|
+
import { ExistsRelationHandler } from './existsRelationHandlers.js';
|
|
24
25
|
function isWhereOperator(val) {
|
|
25
26
|
return _.isString(val) && Object.values(SqlOperator).includes(val.toLowerCase());
|
|
26
27
|
}
|
|
@@ -37,6 +38,12 @@ let Builder = class Builder {
|
|
|
37
38
|
constructor(container, driver, model) {
|
|
38
39
|
this._middlewares = [];
|
|
39
40
|
this._queryMiddlewares = [];
|
|
41
|
+
/**
|
|
42
|
+
* Memoized result of {@link execute}. A builder executes at most once; awaiting
|
|
43
|
+
* it again resolves with the same value instead of re-running the query.
|
|
44
|
+
* Call `clone()` first if you genuinely need a second round-trip.
|
|
45
|
+
*/
|
|
46
|
+
this._executionPromise = null;
|
|
40
47
|
this._driver = driver;
|
|
41
48
|
this._container = container;
|
|
42
49
|
this._model = model;
|
|
@@ -44,82 +51,97 @@ let Builder = class Builder {
|
|
|
44
51
|
this._asRaw = false;
|
|
45
52
|
this._queryMiddlewares = DI.resolve(Array.ofType(QueryMiddleware));
|
|
46
53
|
}
|
|
47
|
-
|
|
48
|
-
|
|
49
|
-
|
|
50
|
-
|
|
51
|
-
|
|
52
|
-
|
|
53
|
-
|
|
54
|
-
|
|
55
|
-
|
|
56
|
-
|
|
57
|
-
|
|
58
|
-
|
|
59
|
-
|
|
60
|
-
|
|
61
|
-
|
|
62
|
-
|
|
63
|
-
|
|
64
|
-
|
|
65
|
-
|
|
66
|
-
|
|
67
|
-
|
|
68
|
-
|
|
69
|
-
|
|
70
|
-
|
|
71
|
-
|
|
72
|
-
|
|
73
|
-
|
|
74
|
-
|
|
75
|
-
|
|
76
|
-
|
|
77
|
-
|
|
78
|
-
|
|
79
|
-
|
|
80
|
-
|
|
81
|
-
|
|
82
|
-
|
|
83
|
-
|
|
84
|
-
|
|
85
|
-
|
|
86
|
-
|
|
87
|
-
|
|
88
|
-
|
|
89
|
-
if (onrejected) {
|
|
90
|
-
onrejected(err);
|
|
91
|
-
}
|
|
92
|
-
else {
|
|
93
|
-
throw err;
|
|
94
|
-
}
|
|
95
|
-
}
|
|
96
|
-
}, onrejected);
|
|
97
|
-
}
|
|
98
|
-
else {
|
|
99
|
-
onfulfilled?.(models);
|
|
54
|
+
/**
|
|
55
|
+
* The execution engine. Sends the compiled query to the driver, applies the
|
|
56
|
+
* result middlewares, hydrates models and awaits the post-hydration middlewares.
|
|
57
|
+
*
|
|
58
|
+
* Always *returns* its value — the caller's promise chain is what propagates it.
|
|
59
|
+
* Subclasses override this (not `execute()`) so that their extra work also lands
|
|
60
|
+
* inside the memo.
|
|
61
|
+
*/
|
|
62
|
+
async _run() {
|
|
63
|
+
// Fires for EVERY builder type, not just SELECT. This used to live on
|
|
64
|
+
// `SelectQueryBuilder._run`, so an INSERT/UPDATE/DELETE never reached a
|
|
65
|
+
// `beforeQueryExecution` hook at all — which silently disabled any middleware that
|
|
66
|
+
// needs the finished query (rbac's insert-ownership check is exactly that: the payload
|
|
67
|
+
// does not exist until `values()` has been called, long after construction).
|
|
68
|
+
this._queryMiddlewares.forEach((x) => x.beforeQueryExecution(this));
|
|
69
|
+
const result = (await this._driver.execute(this));
|
|
70
|
+
// Snapshot the pipeline once, *after* the driver call: compiling the query is what
|
|
71
|
+
// registers the relation middlewares (the driver calls `toDB()`), so this is the first
|
|
72
|
+
// point at which the list is complete. Everything below runs against this immutable copy,
|
|
73
|
+
// so a middleware registered later cannot change the pipeline mid-flight, and — crucially —
|
|
74
|
+
// we never call `Array.prototype.reverse()` on `this._middlewares`, which mutates in place
|
|
75
|
+
// and used to flip `modelCreation` resolution order on every execution ( B8 ).
|
|
76
|
+
const middlewares = [...this._middlewares];
|
|
77
|
+
const creationOrder = [...middlewares].reverse();
|
|
78
|
+
if (this._asRaw) {
|
|
79
|
+
return result;
|
|
80
|
+
}
|
|
81
|
+
const transformedResult = result;
|
|
82
|
+
// if we have something to transform ...
|
|
83
|
+
if (transformedResult) {
|
|
84
|
+
middlewares.forEach((m) => {
|
|
85
|
+
Object.assign(transformedResult, m.afterQuery(transformedResult));
|
|
86
|
+
});
|
|
87
|
+
}
|
|
88
|
+
if (this._model && !this._nonSelect) {
|
|
89
|
+
// TODO: rething this casting
|
|
90
|
+
const models = transformedResult.map((r) => {
|
|
91
|
+
let model = null;
|
|
92
|
+
for (const middleware of creationOrder) {
|
|
93
|
+
model = middleware.modelCreation(r);
|
|
94
|
+
if (model !== null) {
|
|
95
|
+
break;
|
|
100
96
|
}
|
|
101
97
|
}
|
|
102
|
-
|
|
103
|
-
|
|
98
|
+
if (model === null) {
|
|
99
|
+
model = DI.resolve('__orm_model_factory__', [this._model]);
|
|
104
100
|
}
|
|
105
|
-
|
|
106
|
-
|
|
107
|
-
|
|
108
|
-
|
|
109
|
-
|
|
110
|
-
|
|
111
|
-
|
|
101
|
+
model.hydrate(r);
|
|
102
|
+
model.IsDirty = false;
|
|
103
|
+
// This is the one point at which the instance's columns hold exactly what the
|
|
104
|
+
// database returned, so it is the diff baseline for `save()`. Relation members
|
|
105
|
+
// are attached later by the afterHydration middlewares below, which record their
|
|
106
|
+
// own member keys into this same snapshot.
|
|
107
|
+
model.takeSnapshot();
|
|
108
|
+
// Nested relation data that arrived on the row itself ( belongsTo LEFT JOIN,
|
|
109
|
+
// and hasMany arrays passed straight to hydrate ) was attached by the hydrators
|
|
110
|
+
// inside `hydrate()` above — i.e. before the snapshot existed, so their own
|
|
111
|
+
// `snapshotRelation` calls no-opped. Record those relations now.
|
|
112
|
+
for (const name of (model.ModelDescriptor?.Relations ?? new Map()).keys()) {
|
|
113
|
+
if (model[name]?.Populated) {
|
|
114
|
+
model.snapshotRelation(name);
|
|
115
|
+
}
|
|
112
116
|
}
|
|
117
|
+
return model;
|
|
118
|
+
});
|
|
119
|
+
if (middlewares.length > 0) {
|
|
120
|
+
await Promise.all(middlewares.map((m) => m.afterHydration(models)));
|
|
113
121
|
}
|
|
114
|
-
|
|
115
|
-
|
|
116
|
-
|
|
117
|
-
|
|
118
|
-
|
|
119
|
-
|
|
120
|
-
|
|
121
|
-
|
|
122
|
-
|
|
122
|
+
return models;
|
|
123
|
+
}
|
|
124
|
+
return transformedResult;
|
|
125
|
+
}
|
|
126
|
+
/**
|
|
127
|
+
* Executes the query. The single entry point for execution — `then()` delegates here.
|
|
128
|
+
* The underlying work runs exactly once per builder instance; subsequent calls resolve
|
|
129
|
+
* with the memoized result.
|
|
130
|
+
*/
|
|
131
|
+
execute() {
|
|
132
|
+
if (!this._executionPromise) {
|
|
133
|
+
this._executionPromise = this._run();
|
|
134
|
+
}
|
|
135
|
+
return this._executionPromise;
|
|
136
|
+
}
|
|
137
|
+
then(onfulfilled, onrejected) {
|
|
138
|
+
return this.execute().then(onfulfilled, onrejected);
|
|
139
|
+
}
|
|
140
|
+
catch(onrejected) {
|
|
141
|
+
return this.execute().catch(onrejected);
|
|
142
|
+
}
|
|
143
|
+
finally(onfinally) {
|
|
144
|
+
return this.execute().finally(onfinally);
|
|
123
145
|
}
|
|
124
146
|
middleware(middleware) {
|
|
125
147
|
this._middlewares.push(middleware);
|
|
@@ -191,7 +213,7 @@ let QueryBuilder = class QueryBuilder extends Builder {
|
|
|
191
213
|
*
|
|
192
214
|
*/
|
|
193
215
|
setTable(table, alias) {
|
|
194
|
-
if (
|
|
216
|
+
if (isNullOrWhitespace(table)) {
|
|
195
217
|
throw new InvalidArgument('table name is empty');
|
|
196
218
|
}
|
|
197
219
|
this._table = table;
|
|
@@ -228,9 +250,7 @@ let LimitBuilder = class LimitBuilder {
|
|
|
228
250
|
};
|
|
229
251
|
}
|
|
230
252
|
take(count) {
|
|
231
|
-
|
|
232
|
-
throw new InvalidArgument(`take count cannot be negative number`);
|
|
233
|
-
}
|
|
253
|
+
_check_arg(_positive())(count, 'take count');
|
|
234
254
|
this._limit.limit = count;
|
|
235
255
|
return this;
|
|
236
256
|
}
|
|
@@ -256,7 +276,7 @@ let LimitBuilder = class LimitBuilder {
|
|
|
256
276
|
const result = (await this);
|
|
257
277
|
if (result === undefined || (Array.isArray(result) && result.length === 0)) {
|
|
258
278
|
if (typeof error === 'function') {
|
|
259
|
-
|
|
279
|
+
throw error(this.toDB());
|
|
260
280
|
}
|
|
261
281
|
else
|
|
262
282
|
throw error;
|
|
@@ -285,10 +305,7 @@ LimitBuilder = __decorate([
|
|
|
285
305
|
export { LimitBuilder };
|
|
286
306
|
let OrderByBuilder = class OrderByBuilder {
|
|
287
307
|
constructor() {
|
|
288
|
-
this.
|
|
289
|
-
column: '',
|
|
290
|
-
order: SortOrder.ASC,
|
|
291
|
-
};
|
|
308
|
+
this._sorts = [];
|
|
292
309
|
}
|
|
293
310
|
order(column, direction) {
|
|
294
311
|
if (!column) {
|
|
@@ -301,10 +318,10 @@ let OrderByBuilder = class OrderByBuilder {
|
|
|
301
318
|
});
|
|
302
319
|
return this;
|
|
303
320
|
}
|
|
304
|
-
this.
|
|
321
|
+
this._sorts.push({
|
|
305
322
|
column,
|
|
306
323
|
order: direction,
|
|
307
|
-
};
|
|
324
|
+
});
|
|
308
325
|
return this;
|
|
309
326
|
}
|
|
310
327
|
orderBy(column) {
|
|
@@ -315,10 +332,10 @@ let OrderByBuilder = class OrderByBuilder {
|
|
|
315
332
|
});
|
|
316
333
|
return this;
|
|
317
334
|
}
|
|
318
|
-
this.
|
|
335
|
+
this._sorts.push({
|
|
319
336
|
column,
|
|
320
337
|
order: SortOrder.ASC,
|
|
321
|
-
};
|
|
338
|
+
});
|
|
322
339
|
return this;
|
|
323
340
|
}
|
|
324
341
|
orderByDescending(column) {
|
|
@@ -329,14 +346,25 @@ let OrderByBuilder = class OrderByBuilder {
|
|
|
329
346
|
});
|
|
330
347
|
return this;
|
|
331
348
|
}
|
|
332
|
-
this.
|
|
349
|
+
this._sorts.push({
|
|
333
350
|
column,
|
|
334
351
|
order: SortOrder.DESC,
|
|
335
|
-
};
|
|
352
|
+
});
|
|
336
353
|
return this;
|
|
337
354
|
}
|
|
355
|
+
/**
|
|
356
|
+
* Returns the FIRST sort entry (or null) for backward compat with dialect
|
|
357
|
+
* packages that emit a single ORDER BY column. Use getSorts() for all entries.
|
|
358
|
+
*/
|
|
338
359
|
getSort() {
|
|
339
|
-
|
|
360
|
+
const sort = this._sorts.find((s) => s.column.trim() !== '');
|
|
361
|
+
return sort ?? null;
|
|
362
|
+
}
|
|
363
|
+
/**
|
|
364
|
+
* Returns all sort entries (multi-column ORDER BY), skipping empty columns.
|
|
365
|
+
*/
|
|
366
|
+
getSorts() {
|
|
367
|
+
return this._sorts.filter((s) => s.column.trim() !== '');
|
|
340
368
|
}
|
|
341
369
|
};
|
|
342
370
|
OrderByBuilder = __decorate([
|
|
@@ -548,6 +576,23 @@ let WithRecursiveBuilder = class WithRecursiveBuilder {
|
|
|
548
576
|
this._cteStatement = this._container.resolve(WithRecursiveStatement, ['cte', this, rcKeyName, pkName]);
|
|
549
577
|
return this;
|
|
550
578
|
}
|
|
579
|
+
/**
|
|
580
|
+
* Drops the recursive CTE from this builder.
|
|
581
|
+
*
|
|
582
|
+
* `WithRecursiveStatement.build()` compiles two CLONES of the owning query — the anchor
|
|
583
|
+
* member and the recursive member of the CTE. `clone()` copies `_cteStatement`, so each
|
|
584
|
+
* clone was still marked recursive and compiling it re-entered `build()`, which cloned
|
|
585
|
+
* again: an unbounded mutual recursion between `toDB()` and the recursive compiler that
|
|
586
|
+
* ended in a stack overflow rather than a query. The two member queries are by definition
|
|
587
|
+
* not themselves recursive, so the statement clears the flag on its clones.
|
|
588
|
+
*
|
|
589
|
+
* Named alongside `clearJoins()` / `clearWhere()`, which `build()` already uses to strip the
|
|
590
|
+
* parts of the parent query each member must not inherit.
|
|
591
|
+
*/
|
|
592
|
+
clearRecursive() {
|
|
593
|
+
this._cteStatement = undefined;
|
|
594
|
+
return this;
|
|
595
|
+
}
|
|
551
596
|
};
|
|
552
597
|
WithRecursiveBuilder = __decorate([
|
|
553
598
|
NewInstance()
|
|
@@ -582,12 +627,31 @@ let WhereBuilder = WhereBuilder_1 = class WhereBuilder {
|
|
|
582
627
|
clone(_parent) {
|
|
583
628
|
// TODO: fix this cast
|
|
584
629
|
const builder = new WhereBuilder_1(_parent);
|
|
585
|
-
builder._statements = this._statements.map((s) =>
|
|
630
|
+
builder._statements = this._statements.map((s) => {
|
|
631
|
+
const cloned = s.clone(builder);
|
|
632
|
+
// preserve the per-statement boolean connector (clone() rebuilds the
|
|
633
|
+
// statement from scratch and would otherwise reset it to the AND default)
|
|
634
|
+
cloned.Boolean = s.Boolean;
|
|
635
|
+
return cloned;
|
|
636
|
+
});
|
|
586
637
|
builder._boolean = this._boolean;
|
|
587
638
|
builder._model = this._model;
|
|
588
639
|
builder._tableAlias = this.TableAlias;
|
|
589
640
|
return builder;
|
|
590
641
|
}
|
|
642
|
+
/**
|
|
643
|
+
* Pushes a statement onto this builder, stamping it with the currently pending
|
|
644
|
+
* boolean connector (set by {@link orWhere}/{@link andWhere}). The pending
|
|
645
|
+
* connector applies to the NEXT pushed statement only and resets to AND
|
|
646
|
+
* afterwards, so `where(a).where(b).orWhere(c).where(d)` compiles to
|
|
647
|
+
* `a AND b OR c AND d` rather than rewriting the whole clause.
|
|
648
|
+
*/
|
|
649
|
+
pushStatement(statement) {
|
|
650
|
+
statement.Boolean = this._boolean;
|
|
651
|
+
this._statements.push(statement);
|
|
652
|
+
this._boolean = WhereBoolean.AND;
|
|
653
|
+
return this;
|
|
654
|
+
}
|
|
591
655
|
when(condition, callback, callbackElse) {
|
|
592
656
|
if (condition) {
|
|
593
657
|
if (callback)
|
|
@@ -632,18 +696,18 @@ let WhereBuilder = WhereBuilder_1 = class WhereBuilder {
|
|
|
632
696
|
return this.where(RawQuery.create(column ? 'TRUE' : 'FALSE'));
|
|
633
697
|
}
|
|
634
698
|
if (column instanceof RawQuery) {
|
|
635
|
-
this.
|
|
699
|
+
this.pushStatement(this._container.resolve(RawQueryStatement, [column.Query, column.Bindings, self.TableAlias]));
|
|
636
700
|
return this;
|
|
637
701
|
}
|
|
638
702
|
// handle nested where's
|
|
639
703
|
if (_.isFunction(column)) {
|
|
640
704
|
const builder = new WhereBuilder_1(this);
|
|
641
705
|
column.call(builder);
|
|
642
|
-
self.
|
|
706
|
+
self.pushStatement(this._container.resolve(WhereQueryStatement, [builder, self.TableAlias]));
|
|
643
707
|
return this;
|
|
644
708
|
}
|
|
645
709
|
if (column instanceof Lazy) {
|
|
646
|
-
this.
|
|
710
|
+
this.pushStatement(this._container.resolve(LazyQueryStatement, [column, this]));
|
|
647
711
|
return this;
|
|
648
712
|
}
|
|
649
713
|
// handle simple key = object[key] AND ....
|
|
@@ -672,7 +736,7 @@ let WhereBuilder = WhereBuilder_1 = class WhereBuilder {
|
|
|
672
736
|
if (sVal === null) {
|
|
673
737
|
return this.whereNull(c);
|
|
674
738
|
}
|
|
675
|
-
self.
|
|
739
|
+
self.pushStatement(self._container.resolve(WhereStatement, [c, SqlOperator.EQ, sVal, this]));
|
|
676
740
|
return self;
|
|
677
741
|
}
|
|
678
742
|
/**
|
|
@@ -691,12 +755,17 @@ let WhereBuilder = WhereBuilder_1 = class WhereBuilder {
|
|
|
691
755
|
throw new InvalidArgument(`value cannot be NaN`);
|
|
692
756
|
}
|
|
693
757
|
if (sVal === null) {
|
|
694
|
-
|
|
695
|
-
|
|
696
|
-
|
|
697
|
-
|
|
758
|
+
const op = String(o).toLowerCase();
|
|
759
|
+
// where(col, '=', null) => IS NULL ; where(col, '!=' / '<>', null) => IS NOT NULL
|
|
760
|
+
if (op === SqlOperator.EQ) {
|
|
761
|
+
return this.whereNull(c);
|
|
762
|
+
}
|
|
763
|
+
if (op === SqlOperator.NOT || op === SqlOperator.NOT_2) {
|
|
764
|
+
return this.whereNotNull(c);
|
|
765
|
+
}
|
|
766
|
+
throw new InvalidArgument(`operator ${o} cannot be used with null value ( only =, !=, <> are allowed )`);
|
|
698
767
|
}
|
|
699
|
-
self.
|
|
768
|
+
self.pushStatement(self._container.resolve(WhereStatement, [c, o, sVal, self]));
|
|
700
769
|
return this;
|
|
701
770
|
}
|
|
702
771
|
}
|
|
@@ -713,9 +782,9 @@ let WhereBuilder = WhereBuilder_1 = class WhereBuilder {
|
|
|
713
782
|
for (const key of Object.keys(obj).filter((x) => obj[x] !== undefined)) {
|
|
714
783
|
const val = obj[key];
|
|
715
784
|
if (Array.isArray(val)) {
|
|
716
|
-
|
|
717
|
-
|
|
718
|
-
|
|
785
|
+
// empty array => SQL `IN ()` semantics => match nothing (FALSE),
|
|
786
|
+
// never "no condition" (which would match everything)
|
|
787
|
+
this.whereIn(key, val);
|
|
719
788
|
}
|
|
720
789
|
else if (val === null) {
|
|
721
790
|
this.whereNull(key);
|
|
@@ -726,165 +795,82 @@ let WhereBuilder = WhereBuilder_1 = class WhereBuilder {
|
|
|
726
795
|
return this;
|
|
727
796
|
}
|
|
728
797
|
whereNotNull(column) {
|
|
729
|
-
this.
|
|
798
|
+
this.pushStatement(this._container.resolve(WhereStatement, [column, SqlOperator.NOT_NULL, null, this]));
|
|
730
799
|
return this;
|
|
731
800
|
}
|
|
732
801
|
whereNull(column) {
|
|
733
|
-
this.
|
|
802
|
+
this.pushStatement(this._container.resolve(WhereStatement, [column, SqlOperator.NULL, null, this]));
|
|
734
803
|
return this;
|
|
735
804
|
}
|
|
736
805
|
whereNot(column, val) {
|
|
737
806
|
return this.where(column, SqlOperator.NOT, val);
|
|
738
807
|
}
|
|
739
808
|
whereIn(column, val) {
|
|
740
|
-
|
|
809
|
+
// `IN ()` matches nothing in SQL; compile an empty set to FALSE rather than
|
|
810
|
+
// emitting no condition (which would silently match every row).
|
|
811
|
+
if (Array.isArray(val) && val.length === 0) {
|
|
812
|
+
this.where(false);
|
|
813
|
+
return this;
|
|
814
|
+
}
|
|
815
|
+
this.pushStatement(this._container.resolve(InStatement, [column, val, false, this]));
|
|
741
816
|
return this;
|
|
742
817
|
}
|
|
743
818
|
whereNotIn(column, val) {
|
|
744
|
-
this.
|
|
819
|
+
this.pushStatement(this._container.resolve(InStatement, [column, val, true, this]));
|
|
745
820
|
return this;
|
|
746
821
|
}
|
|
747
822
|
whereExist(query, callback) {
|
|
748
|
-
|
|
749
|
-
// TODO: move relation handling to separate DI service for every exists relation type
|
|
750
|
-
let relQuery;
|
|
751
|
-
let sourcePKey = '';
|
|
752
|
-
const self = this;
|
|
753
|
-
let tableName = '';
|
|
754
|
-
let tDesc;
|
|
755
|
-
if (typeof query === 'string') {
|
|
756
|
-
const rel = this._model.getRelationDescriptor(query);
|
|
757
|
-
if (!rel) {
|
|
758
|
-
throw new OrmException(`relation ${query} not found in model ${this.constructor.name}`);
|
|
759
|
-
}
|
|
760
|
-
switch (rel.Type) {
|
|
761
|
-
case RelationType.One:
|
|
762
|
-
this.whereNotNull(rel.ForeignKey);
|
|
763
|
-
// simply use right join for condition check
|
|
764
|
-
if (callback) {
|
|
765
|
-
// TODO: cast fix
|
|
766
|
-
this.rightJoin(rel.TargetModel, callback.bind(query));
|
|
767
|
-
}
|
|
768
|
-
break;
|
|
769
|
-
case RelationType.Many:
|
|
770
|
-
tableName = rel.TargetModel.getModelDescriptor().TableName;
|
|
771
|
-
tDesc = self._model.getModelDescriptor();
|
|
772
|
-
// set alias to avoid conflicts in case of multiple relations to same model and to make sure that relation query is correct even if source query has alias
|
|
773
|
-
relQuery = rel.TargetModel.query().setAlias(`${tableName}_exists`);
|
|
774
|
-
relQuery.where(Lazy.oF(function () {
|
|
775
|
-
const sourceAlias = self._tableAlias ?? (self._parent ? self._parent.TableAlias : tDesc.TableName);
|
|
776
|
-
sourcePKey = `\`${sourceAlias}\`.\`${self._model.getModelDescriptor().PrimaryKey}\``;
|
|
777
|
-
// relQuery is guaranteed assigned above before this lazy callback executes
|
|
778
|
-
relQuery.where(new RawQuery(`${rel.ForeignKey} = ${sourcePKey}`));
|
|
779
|
-
}));
|
|
780
|
-
if (callback) {
|
|
781
|
-
callback.apply(relQuery);
|
|
782
|
-
}
|
|
783
|
-
this.whereExist(relQuery);
|
|
784
|
-
break;
|
|
785
|
-
case RelationType.ManyToMany:
|
|
786
|
-
relQuery = rel.JunctionModel.query();
|
|
787
|
-
tableName = rel.TargetModel.getModelDescriptor().TableName;
|
|
788
|
-
tDesc = self._model.getModelDescriptor();
|
|
789
|
-
relQuery = rel.TargetModel.query().setAlias(`${tableName}_exists`);
|
|
790
|
-
relQuery.where(Lazy.oF(function () {
|
|
791
|
-
const sourceAlias = self._tableAlias ?? (self._parent ? self._parent.TableAlias : tDesc.TableName);
|
|
792
|
-
sourcePKey = `\`${sourceAlias}\`.\`${self._model.getModelDescriptor().PrimaryKey}\``;
|
|
793
|
-
// relQuery is guaranteed assigned above before this lazy callback executes
|
|
794
|
-
relQuery.where(new RawQuery(`${rel.JunctionModelSourceModelFKey_Name} = ${sourcePKey}`));
|
|
795
|
-
}));
|
|
796
|
-
this.setAlias();
|
|
797
|
-
relQuery.rightJoin({
|
|
798
|
-
joinModel: rel.TargetModel,
|
|
799
|
-
joinTableForeignKey: rel.ForeignKey,
|
|
800
|
-
sourceTablePrimaryKey: rel.JunctionModelTargetModelFKey_Name,
|
|
801
|
-
callback: callback,
|
|
802
|
-
});
|
|
803
|
-
this.whereExist(relQuery);
|
|
804
|
-
break;
|
|
805
|
-
}
|
|
806
|
-
}
|
|
807
|
-
else {
|
|
808
|
-
this._statements.push(this._container.resolve(ExistsQueryStatement, [query, false]));
|
|
809
|
-
}
|
|
810
|
-
return this;
|
|
823
|
+
return this.buildExistsClause(query, false, callback);
|
|
811
824
|
}
|
|
812
825
|
whereNotExists(query, callback) {
|
|
813
|
-
|
|
814
|
-
|
|
815
|
-
|
|
816
|
-
|
|
817
|
-
|
|
818
|
-
|
|
819
|
-
|
|
820
|
-
|
|
821
|
-
|
|
822
|
-
|
|
823
|
-
|
|
824
|
-
|
|
825
|
-
|
|
826
|
-
|
|
827
|
-
|
|
828
|
-
|
|
829
|
-
|
|
830
|
-
|
|
831
|
-
break;
|
|
832
|
-
case RelationType.Many:
|
|
833
|
-
tableName = rel.TargetModel.getModelDescriptor().TableName;
|
|
834
|
-
tDesc = self._model.getModelDescriptor();
|
|
835
|
-
// set alias to avoid conflicts in case of multiple relations to same model and to make sure that relation query is correct even if source query has alias
|
|
836
|
-
relQuery = rel.TargetModel.query().setAlias(`${tableName}_exists`);
|
|
837
|
-
relQuery.where(Lazy.oF(function () {
|
|
838
|
-
const sourceAlias = self._tableAlias || (self._parent ? self._parent.TableAlias : tDesc.TableName);
|
|
839
|
-
sourcePKey = `\`${sourceAlias}\`.\`${self._model.getModelDescriptor().PrimaryKey}\``;
|
|
840
|
-
// relQuery is guaranteed assigned above before this lazy callback executes
|
|
841
|
-
relQuery.where(new RawQuery(`${rel.ForeignKey} = ${sourcePKey}`));
|
|
842
|
-
}));
|
|
843
|
-
if (callback) {
|
|
844
|
-
callback.apply(relQuery);
|
|
845
|
-
}
|
|
846
|
-
this.whereNotExists(relQuery);
|
|
847
|
-
break;
|
|
848
|
-
case RelationType.ManyToMany:
|
|
849
|
-
relQuery = rel.JunctionModel.query();
|
|
850
|
-
tableName = rel.TargetModel.getModelDescriptor().TableName;
|
|
851
|
-
tDesc = self._model.getModelDescriptor();
|
|
852
|
-
relQuery = rel.TargetModel.query().setAlias(`${tableName}_exists`);
|
|
853
|
-
relQuery.where(Lazy.oF(function () {
|
|
854
|
-
const sourceAlias = self._tableAlias || (self._parent ? self._parent.TableAlias : tDesc.TableName);
|
|
855
|
-
sourcePKey = `\`${sourceAlias}\`.\`${self._model.getModelDescriptor().PrimaryKey}\``;
|
|
856
|
-
// relQuery is guaranteed assigned above before this lazy callback executes
|
|
857
|
-
relQuery.where(new RawQuery(`${rel.JunctionModelSourceModelFKey_Name} = ${sourcePKey}`));
|
|
858
|
-
}));
|
|
859
|
-
relQuery.rightJoin({
|
|
860
|
-
joinModel: rel.TargetModel,
|
|
861
|
-
joinTableForeignKey: rel.PrimaryKey,
|
|
862
|
-
sourceTablePrimaryKey: rel.ForeignKey,
|
|
863
|
-
callback: callback,
|
|
864
|
-
});
|
|
865
|
-
this.whereNotExists(relQuery);
|
|
866
|
-
break;
|
|
867
|
-
}
|
|
826
|
+
return this.buildExistsClause(query, true, callback);
|
|
827
|
+
}
|
|
828
|
+
/**
|
|
829
|
+
* Shared implementation for {@link whereExist} / {@link whereNotExists}.
|
|
830
|
+
*
|
|
831
|
+
* For a ready sub-query it pushes an {@link ExistsQueryStatement} directly. For a relation
|
|
832
|
+
* name it resolves the matching {@link ExistsRelationHandler} from the container (one per
|
|
833
|
+
* {@link RelationType}) and lets it either mutate this builder or return a correlated
|
|
834
|
+
* sub-query that we then wrap in EXISTS / NOT EXISTS.
|
|
835
|
+
*
|
|
836
|
+
* @param query relation name or a ready sub-query
|
|
837
|
+
* @param negated `true` for NOT EXISTS, `false` for EXISTS
|
|
838
|
+
* @param callback optional where-callback applied to the relation sub-query
|
|
839
|
+
*/
|
|
840
|
+
buildExistsClause(query, negated, callback) {
|
|
841
|
+
if (typeof query !== 'string') {
|
|
842
|
+
this.pushStatement(this._container.resolve(ExistsQueryStatement, [query, negated]));
|
|
843
|
+
return this;
|
|
868
844
|
}
|
|
869
|
-
|
|
870
|
-
|
|
845
|
+
const rel = this._model.getRelationDescriptor(query);
|
|
846
|
+
if (!rel) {
|
|
847
|
+
throw new OrmException(`relation ${query} not found in model ${this.constructor.name}`);
|
|
848
|
+
}
|
|
849
|
+
const handlers = this._container.resolve(Array.ofType(ExistsRelationHandler));
|
|
850
|
+
const handler = handlers.find((h) => h.Type === rel.Type);
|
|
851
|
+
if (!handler) {
|
|
852
|
+
throw new OrmException(`no ExistsRelationHandler registered for relation type ${rel.Type} (relation ${query} on ${this.constructor.name})`);
|
|
853
|
+
}
|
|
854
|
+
const subquery = handler.apply(this, rel, query, callback);
|
|
855
|
+
if (subquery) {
|
|
856
|
+
this.pushStatement(this._container.resolve(ExistsQueryStatement, [subquery, negated]));
|
|
871
857
|
}
|
|
872
858
|
return this;
|
|
873
859
|
}
|
|
874
860
|
whereBetween(column, val) {
|
|
875
|
-
this.
|
|
861
|
+
this.pushStatement(this._container.resolve(BetweenStatement, [column, val, false, this.TableAlias]));
|
|
876
862
|
return this;
|
|
877
863
|
}
|
|
878
864
|
whereNotBetween(column, val) {
|
|
879
|
-
this.
|
|
865
|
+
this.pushStatement(this._container.resolve(BetweenStatement, [column, val, true, this.TableAlias]));
|
|
880
866
|
return this;
|
|
881
867
|
}
|
|
882
868
|
whereInSet(column, val) {
|
|
883
|
-
this.
|
|
869
|
+
this.pushStatement(this._container.resolve(InSetStatement, [column, val, false, this.TableAlias]));
|
|
884
870
|
return this;
|
|
885
871
|
}
|
|
886
872
|
whereNotInSet(column, val) {
|
|
887
|
-
this.
|
|
873
|
+
this.pushStatement(this._container.resolve(InSetStatement, [column, val, true, this.TableAlias]));
|
|
888
874
|
return this;
|
|
889
875
|
}
|
|
890
876
|
clearWhere() {
|
|
@@ -924,10 +910,7 @@ let SelectQueryBuilder = SelectQueryBuilder_1 = class SelectQueryBuilder extends
|
|
|
924
910
|
this._distinct = false;
|
|
925
911
|
this._method = QueryMethod.SELECT;
|
|
926
912
|
this._boolean = WhereBoolean.AND;
|
|
927
|
-
this.
|
|
928
|
-
column: '',
|
|
929
|
-
order: SortOrder.NONE,
|
|
930
|
-
};
|
|
913
|
+
this._sorts = [];
|
|
931
914
|
this._first = false;
|
|
932
915
|
this._limit = {
|
|
933
916
|
limit: -1,
|
|
@@ -942,13 +925,12 @@ let SelectQueryBuilder = SelectQueryBuilder_1 = class SelectQueryBuilder extends
|
|
|
942
925
|
return (await this);
|
|
943
926
|
}
|
|
944
927
|
setAlias(alias) {
|
|
945
|
-
|
|
946
|
-
|
|
947
|
-
|
|
948
|
-
this.
|
|
949
|
-
this.
|
|
950
|
-
this.
|
|
951
|
-
this._statements.forEach((c) => (c.TableAlias = alias));
|
|
928
|
+
// isNullOrWhitespace is not a type predicate; a false result guarantees a non-blank string
|
|
929
|
+
const resolvedAlias = isNullOrWhitespace(alias) ? `${this._driver.Options.AliasSeparator}${this._table}${this._driver.Options.AliasSeparator}` : alias;
|
|
930
|
+
this._tableAlias = resolvedAlias;
|
|
931
|
+
this._columns.forEach((c) => (c.TableAlias = resolvedAlias));
|
|
932
|
+
this._joinStatements.forEach((c) => (c.TableAlias = resolvedAlias));
|
|
933
|
+
this._statements.forEach((c) => (c.TableAlias = resolvedAlias));
|
|
952
934
|
return this;
|
|
953
935
|
}
|
|
954
936
|
clone() {
|
|
@@ -959,11 +941,18 @@ let SelectQueryBuilder = SelectQueryBuilder_1 = class SelectQueryBuilder extends
|
|
|
959
941
|
builder._joinStatements = this._joinStatements.map(c => c.clone(builder));
|
|
960
942
|
// Clone statements with mapped WhereBuilder references
|
|
961
943
|
builder._statements = this._statements.map(c => c.clone(builder));
|
|
944
|
+
// Clone group-by statements (previously dropped, silently losing GROUP BY
|
|
945
|
+
// on a cloned query — eg. orm-api count-pagination clones the builder).
|
|
946
|
+
builder._groupStatements = this._groupStatements.map(c => c.clone(builder));
|
|
947
|
+
// Carry relations and result middlewares over. These hold live objects and
|
|
948
|
+
// are shared the same way mergeRelations()/mergeBuilder() already share them.
|
|
949
|
+
builder._relations = [...this._relations];
|
|
950
|
+
builder._middlewares = [...this._middlewares];
|
|
962
951
|
/**
|
|
963
952
|
* ------------------------------------------------------------------
|
|
964
953
|
*/
|
|
965
954
|
builder._limit = { ...this._limit };
|
|
966
|
-
builder.
|
|
955
|
+
builder._sorts = this._sorts.map((s) => ({ ...s }));
|
|
967
956
|
builder._boolean = this._boolean;
|
|
968
957
|
builder._distinct = this._distinct;
|
|
969
958
|
builder._table = this._table;
|
|
@@ -1062,15 +1051,21 @@ let SelectQueryBuilder = SelectQueryBuilder_1 = class SelectQueryBuilder extends
|
|
|
1062
1051
|
this._getRelationInstance(relation).execute(callback);
|
|
1063
1052
|
return this;
|
|
1064
1053
|
}
|
|
1065
|
-
mergeBuilder(builder) {
|
|
1054
|
+
mergeBuilder(builder, includeStatements = true) {
|
|
1066
1055
|
this._columns = this._columns.concat(builder._columns);
|
|
1067
1056
|
this._cteStatement = builder._cteStatement;
|
|
1068
1057
|
this._distinct = builder._distinct;
|
|
1069
|
-
this
|
|
1070
|
-
|
|
1071
|
-
|
|
1072
|
-
|
|
1073
|
-
|
|
1058
|
+
// Fold the merged builder's sorts into this query's sorts (multi-column
|
|
1059
|
+
// ORDER BY). If the merged builder has no sorts, this keeps our own.
|
|
1060
|
+
this._sorts = this._sorts.concat(builder._sorts.map((s) => ({ ...s })));
|
|
1061
|
+
// `includeStatements: false` is used by JoinStatement so that a join
|
|
1062
|
+
// callback's WHERE conditions are NOT folded into the main query's WHERE
|
|
1063
|
+
// (which silently turns a LEFT JOIN into an inner filter). The join emits
|
|
1064
|
+
// those conditions in its own ON clause instead. Columns/sort are still
|
|
1065
|
+
// merged so join-callback selects (e.g. extra joined columns) keep working.
|
|
1066
|
+
if (includeStatements) {
|
|
1067
|
+
this.mergeStatements(builder);
|
|
1068
|
+
}
|
|
1074
1069
|
}
|
|
1075
1070
|
mergeRelations(builder) {
|
|
1076
1071
|
this._relations = this._relations.concat(builder._relations);
|
|
@@ -1081,6 +1076,21 @@ let SelectQueryBuilder = SelectQueryBuilder_1 = class SelectQueryBuilder extends
|
|
|
1081
1076
|
this._joinStatements = this._joinStatements.concat(builder._joinStatements);
|
|
1082
1077
|
this._statements = this._statements.concat(stms);
|
|
1083
1078
|
}
|
|
1079
|
+
/**
|
|
1080
|
+
* Includes soft-deleted rows in the result set by removing the default
|
|
1081
|
+
* `DeletedAt IS NULL` filter added by createQuery for @SoftDelete models.
|
|
1082
|
+
*/
|
|
1083
|
+
withDeleted() {
|
|
1084
|
+
const descriptor = extractModelDescriptor(this._model);
|
|
1085
|
+
const deletedAt = descriptor?.SoftDelete?.DeletedAt;
|
|
1086
|
+
if (!deletedAt) {
|
|
1087
|
+
return this;
|
|
1088
|
+
}
|
|
1089
|
+
this._statements = this._statements.filter((s) => {
|
|
1090
|
+
return !(s instanceof WhereStatement && s.Column === deletedAt && s.Operator === SqlOperator.NULL);
|
|
1091
|
+
});
|
|
1092
|
+
return this;
|
|
1093
|
+
}
|
|
1084
1094
|
min(column, as) {
|
|
1085
1095
|
this._columns.push(this._container.resolve(ColumnMethodStatement, [column, ColumnMethods.MIN, as, this._tableAlias]));
|
|
1086
1096
|
return this;
|
|
@@ -1122,44 +1132,26 @@ let SelectQueryBuilder = SelectQueryBuilder_1 = class SelectQueryBuilder extends
|
|
|
1122
1132
|
return compiler.compile();
|
|
1123
1133
|
}
|
|
1124
1134
|
async all() {
|
|
1125
|
-
return await this;
|
|
1135
|
+
return await this.execute();
|
|
1126
1136
|
}
|
|
1127
1137
|
async resultExists() {
|
|
1128
|
-
|
|
1129
|
-
|
|
1130
|
-
|
|
1131
|
-
|
|
1132
|
-
|
|
1133
|
-
});
|
|
1134
|
-
}
|
|
1135
|
-
then(onfulfilled, onrejected) {
|
|
1136
|
-
this._queryMiddlewares.forEach((x) => x.beforeQueryExecution(this));
|
|
1137
|
-
return super.then((result) => {
|
|
1138
|
-
if (this._first) {
|
|
1139
|
-
if (Array.isArray(result)) {
|
|
1140
|
-
if (result.length !== 0) {
|
|
1141
|
-
return onfulfilled?.(result[0]);
|
|
1142
|
-
}
|
|
1143
|
-
else {
|
|
1144
|
-
try {
|
|
1145
|
-
return onfulfilled?.(undefined);
|
|
1146
|
-
}
|
|
1147
|
-
catch (err) {
|
|
1148
|
-
onrejected?.(err);
|
|
1149
|
-
}
|
|
1150
|
-
}
|
|
1151
|
-
}
|
|
1152
|
-
else {
|
|
1153
|
-
return onfulfilled?.(result);
|
|
1154
|
-
}
|
|
1155
|
-
}
|
|
1156
|
-
else {
|
|
1157
|
-
return onfulfilled?.(result);
|
|
1158
|
-
}
|
|
1159
|
-
}, onrejected);
|
|
1138
|
+
const res = await this.execute();
|
|
1139
|
+
if (Array.isArray(res)) {
|
|
1140
|
+
return res.length > 0;
|
|
1141
|
+
}
|
|
1142
|
+
return res !== undefined && res !== null;
|
|
1160
1143
|
}
|
|
1161
|
-
|
|
1162
|
-
|
|
1144
|
+
/**
|
|
1145
|
+
* Overrides the engine rather than `execute()` so that the `takeFirst()` unwrapping
|
|
1146
|
+
* happens *inside* the memo. `beforeQueryExecution` is dispatched by `Builder._run()`
|
|
1147
|
+
* for every builder type, so it still fires once per builder rather than once per await.
|
|
1148
|
+
*/
|
|
1149
|
+
async _run() {
|
|
1150
|
+
const result = await super._run();
|
|
1151
|
+
if (this._first && Array.isArray(result)) {
|
|
1152
|
+
return (result.length !== 0 ? result[0] : undefined);
|
|
1153
|
+
}
|
|
1154
|
+
return result;
|
|
1163
1155
|
}
|
|
1164
1156
|
};
|
|
1165
1157
|
__decorate([
|
|
@@ -1184,6 +1176,10 @@ let DeleteQueryBuilder = class DeleteQueryBuilder extends QueryBuilder {
|
|
|
1184
1176
|
offset: -1,
|
|
1185
1177
|
};
|
|
1186
1178
|
this.QueryContext = QueryContext.Delete;
|
|
1179
|
+
// Query middlewares (e.g. rbac ownership enforcement) must run for deletes
|
|
1180
|
+
// too, not only selects — otherwise :own permission constraints are never
|
|
1181
|
+
// applied and any row can be deleted.
|
|
1182
|
+
this._queryMiddlewares.forEach((x) => x.afterQueryCreation(this));
|
|
1187
1183
|
}
|
|
1188
1184
|
toDB() {
|
|
1189
1185
|
return this._container.resolve(DeleteQueryCompiler, [this]).compile();
|
|
@@ -1223,6 +1219,9 @@ let OnDuplicateQueryBuilder = class OnDuplicateQueryBuilder {
|
|
|
1223
1219
|
this._columnsToUpdate = columns;
|
|
1224
1220
|
return this;
|
|
1225
1221
|
}
|
|
1222
|
+
execute() {
|
|
1223
|
+
return this._parent.execute();
|
|
1224
|
+
}
|
|
1226
1225
|
then(onfulfilled, onrejected) {
|
|
1227
1226
|
return this._parent.then(onfulfilled, onrejected);
|
|
1228
1227
|
}
|
|
@@ -1246,6 +1245,10 @@ let UpdateQueryBuilder = class UpdateQueryBuilder extends QueryBuilder {
|
|
|
1246
1245
|
this._boolean = WhereBoolean.AND;
|
|
1247
1246
|
this._statements = [];
|
|
1248
1247
|
this.QueryContext = QueryContext.Update;
|
|
1248
|
+
// Query middlewares (e.g. rbac ownership enforcement) must run for updates
|
|
1249
|
+
// too, not only selects — otherwise :own permission constraints are never
|
|
1250
|
+
// applied and any row can be updated.
|
|
1251
|
+
this._queryMiddlewares.forEach((x) => x.afterQueryCreation(this));
|
|
1249
1252
|
}
|
|
1250
1253
|
in(name) {
|
|
1251
1254
|
this.setTable(name);
|
|
@@ -1281,6 +1284,10 @@ let InsertQueryBuilder = class InsertQueryBuilder extends QueryBuilder {
|
|
|
1281
1284
|
get Replace() {
|
|
1282
1285
|
return this._replace;
|
|
1283
1286
|
}
|
|
1287
|
+
/** Columns requested via {@link returning}. Empty when no RETURNING clause was asked for. */
|
|
1288
|
+
get Returning() {
|
|
1289
|
+
return this._returning ?? [];
|
|
1290
|
+
}
|
|
1284
1291
|
constructor(container, driver, model) {
|
|
1285
1292
|
super(container, driver, model);
|
|
1286
1293
|
this._update = false;
|
|
@@ -1289,6 +1296,33 @@ let InsertQueryBuilder = class InsertQueryBuilder extends QueryBuilder {
|
|
|
1289
1296
|
this._columns = [];
|
|
1290
1297
|
this._values = [];
|
|
1291
1298
|
this.QueryContext = QueryContext.Insert;
|
|
1299
|
+
// Inserts get the same middleware pass as the other three builders. Note that a
|
|
1300
|
+
// middleware which needs to see or amend the row payload must use
|
|
1301
|
+
// `beforeQueryExecution` instead — at construction time `values()` has not been called.
|
|
1302
|
+
this._queryMiddlewares.forEach((x) => x.afterQueryCreation(this));
|
|
1303
|
+
}
|
|
1304
|
+
/**
|
|
1305
|
+
* Forces `column` to `value` on every row of the payload, overwriting whatever the caller
|
|
1306
|
+
* supplied.
|
|
1307
|
+
*
|
|
1308
|
+
* This is the write path for a policy that must not be negotiable — rbac's `createOwn`
|
|
1309
|
+
* ownership stamp. `values()` cannot serve: it appends rows and takes the column list from
|
|
1310
|
+
* its own argument, so calling it a second time adds a row rather than amending the
|
|
1311
|
+
* existing ones.
|
|
1312
|
+
*
|
|
1313
|
+
* @param column - column to overwrite on every row
|
|
1314
|
+
* @param value - value to force
|
|
1315
|
+
*/
|
|
1316
|
+
forceColumn(column, value) {
|
|
1317
|
+
const existing = this._columns.findIndex((c) => !(c.Column instanceof RawQuery) && c.Column === column);
|
|
1318
|
+
if (existing === -1) {
|
|
1319
|
+
this.columns([...this._columns.filter((c) => !(c.Column instanceof RawQuery)).map((c) => c.Column), column]);
|
|
1320
|
+
this._values.forEach((row) => row.push(value));
|
|
1321
|
+
return this;
|
|
1322
|
+
}
|
|
1323
|
+
// eslint-disable-next-line security/detect-object-injection
|
|
1324
|
+
this._values.forEach((row) => (row[existing] = value));
|
|
1325
|
+
return this;
|
|
1292
1326
|
}
|
|
1293
1327
|
/**
|
|
1294
1328
|
* Sets insert to ignore on duplicate
|
|
@@ -1301,8 +1335,21 @@ let InsertQueryBuilder = class InsertQueryBuilder extends QueryBuilder {
|
|
|
1301
1335
|
this._replace = true;
|
|
1302
1336
|
return this;
|
|
1303
1337
|
}
|
|
1338
|
+
/**
|
|
1339
|
+
* Asks the dialect to echo the given columns of every inserted row back.
|
|
1340
|
+
*
|
|
1341
|
+
* @throws NotSupported on drivers whose `supportedFeatures().insertReturning` is false —
|
|
1342
|
+
* silently doing nothing is how this API was a no-op on MySQL and MSSQL for years.
|
|
1343
|
+
*/
|
|
1304
1344
|
returning(columns) {
|
|
1345
|
+
if (!this.Driver.supportedFeatures().insertReturning) {
|
|
1346
|
+
throw new NotSupported(`driver ${this.Driver.Options.Driver} does not support RETURNING on INSERT`);
|
|
1347
|
+
}
|
|
1305
1348
|
this._returning = columns;
|
|
1349
|
+
// onDuplicate() sets Upsert unconditionally and wins if it runs afterwards.
|
|
1350
|
+
if (this.QueryContext === QueryContext.Insert) {
|
|
1351
|
+
this.QueryContext = QueryContext.InsertReturning;
|
|
1352
|
+
}
|
|
1306
1353
|
return this;
|
|
1307
1354
|
}
|
|
1308
1355
|
values(data) {
|
|
@@ -1464,6 +1511,7 @@ let ColumnQueryBuilder = class ColumnQueryBuilder {
|
|
|
1464
1511
|
this.Comment = '';
|
|
1465
1512
|
this.Unique = false;
|
|
1466
1513
|
this.Unsigned = false;
|
|
1514
|
+
this.InlinePrimaryKey = true;
|
|
1467
1515
|
this.Args.push(...args);
|
|
1468
1516
|
}
|
|
1469
1517
|
notNull() {
|
|
@@ -1634,10 +1682,11 @@ AlterTableQueryBuilder = __decorate([
|
|
|
1634
1682
|
export { AlterTableQueryBuilder };
|
|
1635
1683
|
let TableQueryBuilder = class TableQueryBuilder extends QueryBuilder {
|
|
1636
1684
|
/**
|
|
1637
|
-
* Alias for
|
|
1685
|
+
* Alias for binary(name, 16 ) - uuids are stored as 16-byte BINARY to match
|
|
1686
|
+
* the UuidConverter ( which writes a dashed uuid as a 16-byte buffer ).
|
|
1638
1687
|
*/
|
|
1639
1688
|
uuid(name) {
|
|
1640
|
-
return this.
|
|
1689
|
+
return this.binary(name, 16);
|
|
1641
1690
|
}
|
|
1642
1691
|
ifExists() {
|
|
1643
1692
|
this._checkExists = true;
|
|
@@ -2043,6 +2092,16 @@ export function createQuery(model, query, injectModel = true) {
|
|
|
2043
2092
|
}
|
|
2044
2093
|
qr.middleware(new DiscriminationMapMiddleware(dsc));
|
|
2045
2094
|
qr.setTable(dsc.TableName);
|
|
2095
|
+
// Soft-delete read filtering: by default exclude rows that have been soft
|
|
2096
|
+
// deleted (DeletedAt IS NOT NULL). SelectQueryBuilder.withDeleted() removes
|
|
2097
|
+
// this default statement to include soft-deleted rows again.
|
|
2098
|
+
// Guarded on the DeletedAt column actually being present in the model's
|
|
2099
|
+
// reflected columns — a filter on a column the schema does not expose is
|
|
2100
|
+
// impossible anyway, and the guard keeps queries working when table info
|
|
2101
|
+
// has not (yet) surfaced the column.
|
|
2102
|
+
if (qr instanceof SelectQueryBuilder && dsc.SoftDelete?.DeletedAt && dsc.Columns?.some((c) => c.Name === dsc.SoftDelete.DeletedAt)) {
|
|
2103
|
+
qr.whereNull(dsc.SoftDelete.DeletedAt);
|
|
2104
|
+
}
|
|
2046
2105
|
if (driver.Options.Database) {
|
|
2047
2106
|
qr.database(driver.Options.Database);
|
|
2048
2107
|
}
|