@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/cjs/builders.js
CHANGED
|
@@ -29,6 +29,7 @@ const relations_js_1 = require("./relations.js");
|
|
|
29
29
|
const util_1 = require("@spinajs/util");
|
|
30
30
|
const discrimination_middleware_js_1 = require("./discrimination-middleware.js");
|
|
31
31
|
const descriptor_js_1 = require("./descriptor.js");
|
|
32
|
+
const existsRelationHandlers_js_1 = require("./existsRelationHandlers.js");
|
|
32
33
|
function isWhereOperator(val) {
|
|
33
34
|
return lodash_1.default.isString(val) && Object.values(enums_js_1.SqlOperator).includes(val.toLowerCase());
|
|
34
35
|
}
|
|
@@ -45,6 +46,12 @@ let Builder = class Builder {
|
|
|
45
46
|
constructor(container, driver, model) {
|
|
46
47
|
this._middlewares = [];
|
|
47
48
|
this._queryMiddlewares = [];
|
|
49
|
+
/**
|
|
50
|
+
* Memoized result of {@link execute}. A builder executes at most once; awaiting
|
|
51
|
+
* it again resolves with the same value instead of re-running the query.
|
|
52
|
+
* Call `clone()` first if you genuinely need a second round-trip.
|
|
53
|
+
*/
|
|
54
|
+
this._executionPromise = null;
|
|
48
55
|
this._driver = driver;
|
|
49
56
|
this._container = container;
|
|
50
57
|
this._model = model;
|
|
@@ -52,82 +59,97 @@ let Builder = class Builder {
|
|
|
52
59
|
this._asRaw = false;
|
|
53
60
|
this._queryMiddlewares = di_1.DI.resolve(Array.ofType(interfaces_js_1.QueryMiddleware));
|
|
54
61
|
}
|
|
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
|
-
|
|
90
|
-
|
|
91
|
-
|
|
92
|
-
|
|
93
|
-
|
|
94
|
-
|
|
95
|
-
|
|
96
|
-
|
|
97
|
-
if (onrejected) {
|
|
98
|
-
onrejected(err);
|
|
99
|
-
}
|
|
100
|
-
else {
|
|
101
|
-
throw err;
|
|
102
|
-
}
|
|
103
|
-
}
|
|
104
|
-
}, onrejected);
|
|
105
|
-
}
|
|
106
|
-
else {
|
|
107
|
-
onfulfilled?.(models);
|
|
62
|
+
/**
|
|
63
|
+
* The execution engine. Sends the compiled query to the driver, applies the
|
|
64
|
+
* result middlewares, hydrates models and awaits the post-hydration middlewares.
|
|
65
|
+
*
|
|
66
|
+
* Always *returns* its value — the caller's promise chain is what propagates it.
|
|
67
|
+
* Subclasses override this (not `execute()`) so that their extra work also lands
|
|
68
|
+
* inside the memo.
|
|
69
|
+
*/
|
|
70
|
+
async _run() {
|
|
71
|
+
// Fires for EVERY builder type, not just SELECT. This used to live on
|
|
72
|
+
// `SelectQueryBuilder._run`, so an INSERT/UPDATE/DELETE never reached a
|
|
73
|
+
// `beforeQueryExecution` hook at all — which silently disabled any middleware that
|
|
74
|
+
// needs the finished query (rbac's insert-ownership check is exactly that: the payload
|
|
75
|
+
// does not exist until `values()` has been called, long after construction).
|
|
76
|
+
this._queryMiddlewares.forEach((x) => x.beforeQueryExecution(this));
|
|
77
|
+
const result = (await this._driver.execute(this));
|
|
78
|
+
// Snapshot the pipeline once, *after* the driver call: compiling the query is what
|
|
79
|
+
// registers the relation middlewares (the driver calls `toDB()`), so this is the first
|
|
80
|
+
// point at which the list is complete. Everything below runs against this immutable copy,
|
|
81
|
+
// so a middleware registered later cannot change the pipeline mid-flight, and — crucially —
|
|
82
|
+
// we never call `Array.prototype.reverse()` on `this._middlewares`, which mutates in place
|
|
83
|
+
// and used to flip `modelCreation` resolution order on every execution ( B8 ).
|
|
84
|
+
const middlewares = [...this._middlewares];
|
|
85
|
+
const creationOrder = [...middlewares].reverse();
|
|
86
|
+
if (this._asRaw) {
|
|
87
|
+
return result;
|
|
88
|
+
}
|
|
89
|
+
const transformedResult = result;
|
|
90
|
+
// if we have something to transform ...
|
|
91
|
+
if (transformedResult) {
|
|
92
|
+
middlewares.forEach((m) => {
|
|
93
|
+
Object.assign(transformedResult, m.afterQuery(transformedResult));
|
|
94
|
+
});
|
|
95
|
+
}
|
|
96
|
+
if (this._model && !this._nonSelect) {
|
|
97
|
+
// TODO: rething this casting
|
|
98
|
+
const models = transformedResult.map((r) => {
|
|
99
|
+
let model = null;
|
|
100
|
+
for (const middleware of creationOrder) {
|
|
101
|
+
model = middleware.modelCreation(r);
|
|
102
|
+
if (model !== null) {
|
|
103
|
+
break;
|
|
108
104
|
}
|
|
109
105
|
}
|
|
110
|
-
|
|
111
|
-
|
|
106
|
+
if (model === null) {
|
|
107
|
+
model = di_1.DI.resolve('__orm_model_factory__', [this._model]);
|
|
112
108
|
}
|
|
113
|
-
|
|
114
|
-
|
|
115
|
-
|
|
116
|
-
|
|
117
|
-
|
|
118
|
-
|
|
119
|
-
|
|
109
|
+
model.hydrate(r);
|
|
110
|
+
model.IsDirty = false;
|
|
111
|
+
// This is the one point at which the instance's columns hold exactly what the
|
|
112
|
+
// database returned, so it is the diff baseline for `save()`. Relation members
|
|
113
|
+
// are attached later by the afterHydration middlewares below, which record their
|
|
114
|
+
// own member keys into this same snapshot.
|
|
115
|
+
model.takeSnapshot();
|
|
116
|
+
// Nested relation data that arrived on the row itself ( belongsTo LEFT JOIN,
|
|
117
|
+
// and hasMany arrays passed straight to hydrate ) was attached by the hydrators
|
|
118
|
+
// inside `hydrate()` above — i.e. before the snapshot existed, so their own
|
|
119
|
+
// `snapshotRelation` calls no-opped. Record those relations now.
|
|
120
|
+
for (const name of (model.ModelDescriptor?.Relations ?? new Map()).keys()) {
|
|
121
|
+
if (model[name]?.Populated) {
|
|
122
|
+
model.snapshotRelation(name);
|
|
123
|
+
}
|
|
120
124
|
}
|
|
125
|
+
return model;
|
|
126
|
+
});
|
|
127
|
+
if (middlewares.length > 0) {
|
|
128
|
+
await Promise.all(middlewares.map((m) => m.afterHydration(models)));
|
|
121
129
|
}
|
|
122
|
-
|
|
123
|
-
|
|
124
|
-
|
|
125
|
-
|
|
126
|
-
|
|
127
|
-
|
|
128
|
-
|
|
129
|
-
|
|
130
|
-
|
|
130
|
+
return models;
|
|
131
|
+
}
|
|
132
|
+
return transformedResult;
|
|
133
|
+
}
|
|
134
|
+
/**
|
|
135
|
+
* Executes the query. The single entry point for execution — `then()` delegates here.
|
|
136
|
+
* The underlying work runs exactly once per builder instance; subsequent calls resolve
|
|
137
|
+
* with the memoized result.
|
|
138
|
+
*/
|
|
139
|
+
execute() {
|
|
140
|
+
if (!this._executionPromise) {
|
|
141
|
+
this._executionPromise = this._run();
|
|
142
|
+
}
|
|
143
|
+
return this._executionPromise;
|
|
144
|
+
}
|
|
145
|
+
then(onfulfilled, onrejected) {
|
|
146
|
+
return this.execute().then(onfulfilled, onrejected);
|
|
147
|
+
}
|
|
148
|
+
catch(onrejected) {
|
|
149
|
+
return this.execute().catch(onrejected);
|
|
150
|
+
}
|
|
151
|
+
finally(onfinally) {
|
|
152
|
+
return this.execute().finally(onfinally);
|
|
131
153
|
}
|
|
132
154
|
middleware(middleware) {
|
|
133
155
|
this._middlewares.push(middleware);
|
|
@@ -199,7 +221,7 @@ let QueryBuilder = class QueryBuilder extends Builder {
|
|
|
199
221
|
*
|
|
200
222
|
*/
|
|
201
223
|
setTable(table, alias) {
|
|
202
|
-
if (
|
|
224
|
+
if ((0, util_1.isNullOrWhitespace)(table)) {
|
|
203
225
|
throw new exceptions_1.InvalidArgument('table name is empty');
|
|
204
226
|
}
|
|
205
227
|
this._table = table;
|
|
@@ -236,9 +258,7 @@ let LimitBuilder = class LimitBuilder {
|
|
|
236
258
|
};
|
|
237
259
|
}
|
|
238
260
|
take(count) {
|
|
239
|
-
|
|
240
|
-
throw new exceptions_1.InvalidArgument(`take count cannot be negative number`);
|
|
241
|
-
}
|
|
261
|
+
(0, util_1._check_arg)((0, util_1._positive)())(count, 'take count');
|
|
242
262
|
this._limit.limit = count;
|
|
243
263
|
return this;
|
|
244
264
|
}
|
|
@@ -264,7 +284,7 @@ let LimitBuilder = class LimitBuilder {
|
|
|
264
284
|
const result = (await this);
|
|
265
285
|
if (result === undefined || (Array.isArray(result) && result.length === 0)) {
|
|
266
286
|
if (typeof error === 'function') {
|
|
267
|
-
|
|
287
|
+
throw error(this.toDB());
|
|
268
288
|
}
|
|
269
289
|
else
|
|
270
290
|
throw error;
|
|
@@ -293,10 +313,7 @@ exports.LimitBuilder = LimitBuilder = __decorate([
|
|
|
293
313
|
], LimitBuilder);
|
|
294
314
|
let OrderByBuilder = class OrderByBuilder {
|
|
295
315
|
constructor() {
|
|
296
|
-
this.
|
|
297
|
-
column: '',
|
|
298
|
-
order: enums_js_1.SortOrder.ASC,
|
|
299
|
-
};
|
|
316
|
+
this._sorts = [];
|
|
300
317
|
}
|
|
301
318
|
order(column, direction) {
|
|
302
319
|
if (!column) {
|
|
@@ -309,10 +326,10 @@ let OrderByBuilder = class OrderByBuilder {
|
|
|
309
326
|
});
|
|
310
327
|
return this;
|
|
311
328
|
}
|
|
312
|
-
this.
|
|
329
|
+
this._sorts.push({
|
|
313
330
|
column,
|
|
314
331
|
order: direction,
|
|
315
|
-
};
|
|
332
|
+
});
|
|
316
333
|
return this;
|
|
317
334
|
}
|
|
318
335
|
orderBy(column) {
|
|
@@ -323,10 +340,10 @@ let OrderByBuilder = class OrderByBuilder {
|
|
|
323
340
|
});
|
|
324
341
|
return this;
|
|
325
342
|
}
|
|
326
|
-
this.
|
|
343
|
+
this._sorts.push({
|
|
327
344
|
column,
|
|
328
345
|
order: enums_js_1.SortOrder.ASC,
|
|
329
|
-
};
|
|
346
|
+
});
|
|
330
347
|
return this;
|
|
331
348
|
}
|
|
332
349
|
orderByDescending(column) {
|
|
@@ -337,14 +354,25 @@ let OrderByBuilder = class OrderByBuilder {
|
|
|
337
354
|
});
|
|
338
355
|
return this;
|
|
339
356
|
}
|
|
340
|
-
this.
|
|
357
|
+
this._sorts.push({
|
|
341
358
|
column,
|
|
342
359
|
order: enums_js_1.SortOrder.DESC,
|
|
343
|
-
};
|
|
360
|
+
});
|
|
344
361
|
return this;
|
|
345
362
|
}
|
|
363
|
+
/**
|
|
364
|
+
* Returns the FIRST sort entry (or null) for backward compat with dialect
|
|
365
|
+
* packages that emit a single ORDER BY column. Use getSorts() for all entries.
|
|
366
|
+
*/
|
|
346
367
|
getSort() {
|
|
347
|
-
|
|
368
|
+
const sort = this._sorts.find((s) => s.column.trim() !== '');
|
|
369
|
+
return sort ?? null;
|
|
370
|
+
}
|
|
371
|
+
/**
|
|
372
|
+
* Returns all sort entries (multi-column ORDER BY), skipping empty columns.
|
|
373
|
+
*/
|
|
374
|
+
getSorts() {
|
|
375
|
+
return this._sorts.filter((s) => s.column.trim() !== '');
|
|
348
376
|
}
|
|
349
377
|
};
|
|
350
378
|
exports.OrderByBuilder = OrderByBuilder;
|
|
@@ -556,6 +584,23 @@ let WithRecursiveBuilder = class WithRecursiveBuilder {
|
|
|
556
584
|
this._cteStatement = this._container.resolve(statements_js_1.WithRecursiveStatement, ['cte', this, rcKeyName, pkName]);
|
|
557
585
|
return this;
|
|
558
586
|
}
|
|
587
|
+
/**
|
|
588
|
+
* Drops the recursive CTE from this builder.
|
|
589
|
+
*
|
|
590
|
+
* `WithRecursiveStatement.build()` compiles two CLONES of the owning query — the anchor
|
|
591
|
+
* member and the recursive member of the CTE. `clone()` copies `_cteStatement`, so each
|
|
592
|
+
* clone was still marked recursive and compiling it re-entered `build()`, which cloned
|
|
593
|
+
* again: an unbounded mutual recursion between `toDB()` and the recursive compiler that
|
|
594
|
+
* ended in a stack overflow rather than a query. The two member queries are by definition
|
|
595
|
+
* not themselves recursive, so the statement clears the flag on its clones.
|
|
596
|
+
*
|
|
597
|
+
* Named alongside `clearJoins()` / `clearWhere()`, which `build()` already uses to strip the
|
|
598
|
+
* parts of the parent query each member must not inherit.
|
|
599
|
+
*/
|
|
600
|
+
clearRecursive() {
|
|
601
|
+
this._cteStatement = undefined;
|
|
602
|
+
return this;
|
|
603
|
+
}
|
|
559
604
|
};
|
|
560
605
|
exports.WithRecursiveBuilder = WithRecursiveBuilder;
|
|
561
606
|
exports.WithRecursiveBuilder = WithRecursiveBuilder = __decorate([
|
|
@@ -590,12 +635,31 @@ let WhereBuilder = WhereBuilder_1 = class WhereBuilder {
|
|
|
590
635
|
clone(_parent) {
|
|
591
636
|
// TODO: fix this cast
|
|
592
637
|
const builder = new WhereBuilder_1(_parent);
|
|
593
|
-
builder._statements = this._statements.map((s) =>
|
|
638
|
+
builder._statements = this._statements.map((s) => {
|
|
639
|
+
const cloned = s.clone(builder);
|
|
640
|
+
// preserve the per-statement boolean connector (clone() rebuilds the
|
|
641
|
+
// statement from scratch and would otherwise reset it to the AND default)
|
|
642
|
+
cloned.Boolean = s.Boolean;
|
|
643
|
+
return cloned;
|
|
644
|
+
});
|
|
594
645
|
builder._boolean = this._boolean;
|
|
595
646
|
builder._model = this._model;
|
|
596
647
|
builder._tableAlias = this.TableAlias;
|
|
597
648
|
return builder;
|
|
598
649
|
}
|
|
650
|
+
/**
|
|
651
|
+
* Pushes a statement onto this builder, stamping it with the currently pending
|
|
652
|
+
* boolean connector (set by {@link orWhere}/{@link andWhere}). The pending
|
|
653
|
+
* connector applies to the NEXT pushed statement only and resets to AND
|
|
654
|
+
* afterwards, so `where(a).where(b).orWhere(c).where(d)` compiles to
|
|
655
|
+
* `a AND b OR c AND d` rather than rewriting the whole clause.
|
|
656
|
+
*/
|
|
657
|
+
pushStatement(statement) {
|
|
658
|
+
statement.Boolean = this._boolean;
|
|
659
|
+
this._statements.push(statement);
|
|
660
|
+
this._boolean = enums_js_1.WhereBoolean.AND;
|
|
661
|
+
return this;
|
|
662
|
+
}
|
|
599
663
|
when(condition, callback, callbackElse) {
|
|
600
664
|
if (condition) {
|
|
601
665
|
if (callback)
|
|
@@ -640,18 +704,18 @@ let WhereBuilder = WhereBuilder_1 = class WhereBuilder {
|
|
|
640
704
|
return this.where(RawQuery.create(column ? 'TRUE' : 'FALSE'));
|
|
641
705
|
}
|
|
642
706
|
if (column instanceof RawQuery) {
|
|
643
|
-
this.
|
|
707
|
+
this.pushStatement(this._container.resolve(statements_js_1.RawQueryStatement, [column.Query, column.Bindings, self.TableAlias]));
|
|
644
708
|
return this;
|
|
645
709
|
}
|
|
646
710
|
// handle nested where's
|
|
647
711
|
if (lodash_1.default.isFunction(column)) {
|
|
648
712
|
const builder = new WhereBuilder_1(this);
|
|
649
713
|
column.call(builder);
|
|
650
|
-
self.
|
|
714
|
+
self.pushStatement(this._container.resolve(statements_js_1.WhereQueryStatement, [builder, self.TableAlias]));
|
|
651
715
|
return this;
|
|
652
716
|
}
|
|
653
717
|
if (column instanceof util_1.Lazy) {
|
|
654
|
-
this.
|
|
718
|
+
this.pushStatement(this._container.resolve(statements_js_1.LazyQueryStatement, [column, this]));
|
|
655
719
|
return this;
|
|
656
720
|
}
|
|
657
721
|
// handle simple key = object[key] AND ....
|
|
@@ -680,7 +744,7 @@ let WhereBuilder = WhereBuilder_1 = class WhereBuilder {
|
|
|
680
744
|
if (sVal === null) {
|
|
681
745
|
return this.whereNull(c);
|
|
682
746
|
}
|
|
683
|
-
self.
|
|
747
|
+
self.pushStatement(self._container.resolve(statements_js_1.WhereStatement, [c, enums_js_1.SqlOperator.EQ, sVal, this]));
|
|
684
748
|
return self;
|
|
685
749
|
}
|
|
686
750
|
/**
|
|
@@ -699,12 +763,17 @@ let WhereBuilder = WhereBuilder_1 = class WhereBuilder {
|
|
|
699
763
|
throw new exceptions_1.InvalidArgument(`value cannot be NaN`);
|
|
700
764
|
}
|
|
701
765
|
if (sVal === null) {
|
|
702
|
-
|
|
703
|
-
|
|
704
|
-
|
|
705
|
-
|
|
766
|
+
const op = String(o).toLowerCase();
|
|
767
|
+
// where(col, '=', null) => IS NULL ; where(col, '!=' / '<>', null) => IS NOT NULL
|
|
768
|
+
if (op === enums_js_1.SqlOperator.EQ) {
|
|
769
|
+
return this.whereNull(c);
|
|
770
|
+
}
|
|
771
|
+
if (op === enums_js_1.SqlOperator.NOT || op === enums_js_1.SqlOperator.NOT_2) {
|
|
772
|
+
return this.whereNotNull(c);
|
|
773
|
+
}
|
|
774
|
+
throw new exceptions_1.InvalidArgument(`operator ${o} cannot be used with null value ( only =, !=, <> are allowed )`);
|
|
706
775
|
}
|
|
707
|
-
self.
|
|
776
|
+
self.pushStatement(self._container.resolve(statements_js_1.WhereStatement, [c, o, sVal, self]));
|
|
708
777
|
return this;
|
|
709
778
|
}
|
|
710
779
|
}
|
|
@@ -721,9 +790,9 @@ let WhereBuilder = WhereBuilder_1 = class WhereBuilder {
|
|
|
721
790
|
for (const key of Object.keys(obj).filter((x) => obj[x] !== undefined)) {
|
|
722
791
|
const val = obj[key];
|
|
723
792
|
if (Array.isArray(val)) {
|
|
724
|
-
|
|
725
|
-
|
|
726
|
-
|
|
793
|
+
// empty array => SQL `IN ()` semantics => match nothing (FALSE),
|
|
794
|
+
// never "no condition" (which would match everything)
|
|
795
|
+
this.whereIn(key, val);
|
|
727
796
|
}
|
|
728
797
|
else if (val === null) {
|
|
729
798
|
this.whereNull(key);
|
|
@@ -734,165 +803,82 @@ let WhereBuilder = WhereBuilder_1 = class WhereBuilder {
|
|
|
734
803
|
return this;
|
|
735
804
|
}
|
|
736
805
|
whereNotNull(column) {
|
|
737
|
-
this.
|
|
806
|
+
this.pushStatement(this._container.resolve(statements_js_1.WhereStatement, [column, enums_js_1.SqlOperator.NOT_NULL, null, this]));
|
|
738
807
|
return this;
|
|
739
808
|
}
|
|
740
809
|
whereNull(column) {
|
|
741
|
-
this.
|
|
810
|
+
this.pushStatement(this._container.resolve(statements_js_1.WhereStatement, [column, enums_js_1.SqlOperator.NULL, null, this]));
|
|
742
811
|
return this;
|
|
743
812
|
}
|
|
744
813
|
whereNot(column, val) {
|
|
745
814
|
return this.where(column, enums_js_1.SqlOperator.NOT, val);
|
|
746
815
|
}
|
|
747
816
|
whereIn(column, val) {
|
|
748
|
-
|
|
817
|
+
// `IN ()` matches nothing in SQL; compile an empty set to FALSE rather than
|
|
818
|
+
// emitting no condition (which would silently match every row).
|
|
819
|
+
if (Array.isArray(val) && val.length === 0) {
|
|
820
|
+
this.where(false);
|
|
821
|
+
return this;
|
|
822
|
+
}
|
|
823
|
+
this.pushStatement(this._container.resolve(statements_js_1.InStatement, [column, val, false, this]));
|
|
749
824
|
return this;
|
|
750
825
|
}
|
|
751
826
|
whereNotIn(column, val) {
|
|
752
|
-
this.
|
|
827
|
+
this.pushStatement(this._container.resolve(statements_js_1.InStatement, [column, val, true, this]));
|
|
753
828
|
return this;
|
|
754
829
|
}
|
|
755
830
|
whereExist(query, callback) {
|
|
756
|
-
|
|
757
|
-
// TODO: move relation handling to separate DI service for every exists relation type
|
|
758
|
-
let relQuery;
|
|
759
|
-
let sourcePKey = '';
|
|
760
|
-
const self = this;
|
|
761
|
-
let tableName = '';
|
|
762
|
-
let tDesc;
|
|
763
|
-
if (typeof query === 'string') {
|
|
764
|
-
const rel = this._model.getRelationDescriptor(query);
|
|
765
|
-
if (!rel) {
|
|
766
|
-
throw new exceptions_js_1.OrmException(`relation ${query} not found in model ${this.constructor.name}`);
|
|
767
|
-
}
|
|
768
|
-
switch (rel.Type) {
|
|
769
|
-
case interfaces_js_1.RelationType.One:
|
|
770
|
-
this.whereNotNull(rel.ForeignKey);
|
|
771
|
-
// simply use right join for condition check
|
|
772
|
-
if (callback) {
|
|
773
|
-
// TODO: cast fix
|
|
774
|
-
this.rightJoin(rel.TargetModel, callback.bind(query));
|
|
775
|
-
}
|
|
776
|
-
break;
|
|
777
|
-
case interfaces_js_1.RelationType.Many:
|
|
778
|
-
tableName = rel.TargetModel.getModelDescriptor().TableName;
|
|
779
|
-
tDesc = self._model.getModelDescriptor();
|
|
780
|
-
// 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
|
|
781
|
-
relQuery = rel.TargetModel.query().setAlias(`${tableName}_exists`);
|
|
782
|
-
relQuery.where(util_1.Lazy.oF(function () {
|
|
783
|
-
const sourceAlias = self._tableAlias ?? (self._parent ? self._parent.TableAlias : tDesc.TableName);
|
|
784
|
-
sourcePKey = `\`${sourceAlias}\`.\`${self._model.getModelDescriptor().PrimaryKey}\``;
|
|
785
|
-
// relQuery is guaranteed assigned above before this lazy callback executes
|
|
786
|
-
relQuery.where(new RawQuery(`${rel.ForeignKey} = ${sourcePKey}`));
|
|
787
|
-
}));
|
|
788
|
-
if (callback) {
|
|
789
|
-
callback.apply(relQuery);
|
|
790
|
-
}
|
|
791
|
-
this.whereExist(relQuery);
|
|
792
|
-
break;
|
|
793
|
-
case interfaces_js_1.RelationType.ManyToMany:
|
|
794
|
-
relQuery = rel.JunctionModel.query();
|
|
795
|
-
tableName = rel.TargetModel.getModelDescriptor().TableName;
|
|
796
|
-
tDesc = self._model.getModelDescriptor();
|
|
797
|
-
relQuery = rel.TargetModel.query().setAlias(`${tableName}_exists`);
|
|
798
|
-
relQuery.where(util_1.Lazy.oF(function () {
|
|
799
|
-
const sourceAlias = self._tableAlias ?? (self._parent ? self._parent.TableAlias : tDesc.TableName);
|
|
800
|
-
sourcePKey = `\`${sourceAlias}\`.\`${self._model.getModelDescriptor().PrimaryKey}\``;
|
|
801
|
-
// relQuery is guaranteed assigned above before this lazy callback executes
|
|
802
|
-
relQuery.where(new RawQuery(`${rel.JunctionModelSourceModelFKey_Name} = ${sourcePKey}`));
|
|
803
|
-
}));
|
|
804
|
-
this.setAlias();
|
|
805
|
-
relQuery.rightJoin({
|
|
806
|
-
joinModel: rel.TargetModel,
|
|
807
|
-
joinTableForeignKey: rel.ForeignKey,
|
|
808
|
-
sourceTablePrimaryKey: rel.JunctionModelTargetModelFKey_Name,
|
|
809
|
-
callback: callback,
|
|
810
|
-
});
|
|
811
|
-
this.whereExist(relQuery);
|
|
812
|
-
break;
|
|
813
|
-
}
|
|
814
|
-
}
|
|
815
|
-
else {
|
|
816
|
-
this._statements.push(this._container.resolve(statements_js_1.ExistsQueryStatement, [query, false]));
|
|
817
|
-
}
|
|
818
|
-
return this;
|
|
831
|
+
return this.buildExistsClause(query, false, callback);
|
|
819
832
|
}
|
|
820
833
|
whereNotExists(query, callback) {
|
|
821
|
-
|
|
822
|
-
|
|
823
|
-
|
|
824
|
-
|
|
825
|
-
|
|
826
|
-
|
|
827
|
-
|
|
828
|
-
|
|
829
|
-
|
|
830
|
-
|
|
831
|
-
|
|
832
|
-
|
|
833
|
-
|
|
834
|
-
|
|
835
|
-
|
|
836
|
-
|
|
837
|
-
|
|
838
|
-
|
|
839
|
-
break;
|
|
840
|
-
case interfaces_js_1.RelationType.Many:
|
|
841
|
-
tableName = rel.TargetModel.getModelDescriptor().TableName;
|
|
842
|
-
tDesc = self._model.getModelDescriptor();
|
|
843
|
-
// 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
|
|
844
|
-
relQuery = rel.TargetModel.query().setAlias(`${tableName}_exists`);
|
|
845
|
-
relQuery.where(util_1.Lazy.oF(function () {
|
|
846
|
-
const sourceAlias = self._tableAlias || (self._parent ? self._parent.TableAlias : tDesc.TableName);
|
|
847
|
-
sourcePKey = `\`${sourceAlias}\`.\`${self._model.getModelDescriptor().PrimaryKey}\``;
|
|
848
|
-
// relQuery is guaranteed assigned above before this lazy callback executes
|
|
849
|
-
relQuery.where(new RawQuery(`${rel.ForeignKey} = ${sourcePKey}`));
|
|
850
|
-
}));
|
|
851
|
-
if (callback) {
|
|
852
|
-
callback.apply(relQuery);
|
|
853
|
-
}
|
|
854
|
-
this.whereNotExists(relQuery);
|
|
855
|
-
break;
|
|
856
|
-
case interfaces_js_1.RelationType.ManyToMany:
|
|
857
|
-
relQuery = rel.JunctionModel.query();
|
|
858
|
-
tableName = rel.TargetModel.getModelDescriptor().TableName;
|
|
859
|
-
tDesc = self._model.getModelDescriptor();
|
|
860
|
-
relQuery = rel.TargetModel.query().setAlias(`${tableName}_exists`);
|
|
861
|
-
relQuery.where(util_1.Lazy.oF(function () {
|
|
862
|
-
const sourceAlias = self._tableAlias || (self._parent ? self._parent.TableAlias : tDesc.TableName);
|
|
863
|
-
sourcePKey = `\`${sourceAlias}\`.\`${self._model.getModelDescriptor().PrimaryKey}\``;
|
|
864
|
-
// relQuery is guaranteed assigned above before this lazy callback executes
|
|
865
|
-
relQuery.where(new RawQuery(`${rel.JunctionModelSourceModelFKey_Name} = ${sourcePKey}`));
|
|
866
|
-
}));
|
|
867
|
-
relQuery.rightJoin({
|
|
868
|
-
joinModel: rel.TargetModel,
|
|
869
|
-
joinTableForeignKey: rel.PrimaryKey,
|
|
870
|
-
sourceTablePrimaryKey: rel.ForeignKey,
|
|
871
|
-
callback: callback,
|
|
872
|
-
});
|
|
873
|
-
this.whereNotExists(relQuery);
|
|
874
|
-
break;
|
|
875
|
-
}
|
|
834
|
+
return this.buildExistsClause(query, true, callback);
|
|
835
|
+
}
|
|
836
|
+
/**
|
|
837
|
+
* Shared implementation for {@link whereExist} / {@link whereNotExists}.
|
|
838
|
+
*
|
|
839
|
+
* For a ready sub-query it pushes an {@link ExistsQueryStatement} directly. For a relation
|
|
840
|
+
* name it resolves the matching {@link ExistsRelationHandler} from the container (one per
|
|
841
|
+
* {@link RelationType}) and lets it either mutate this builder or return a correlated
|
|
842
|
+
* sub-query that we then wrap in EXISTS / NOT EXISTS.
|
|
843
|
+
*
|
|
844
|
+
* @param query relation name or a ready sub-query
|
|
845
|
+
* @param negated `true` for NOT EXISTS, `false` for EXISTS
|
|
846
|
+
* @param callback optional where-callback applied to the relation sub-query
|
|
847
|
+
*/
|
|
848
|
+
buildExistsClause(query, negated, callback) {
|
|
849
|
+
if (typeof query !== 'string') {
|
|
850
|
+
this.pushStatement(this._container.resolve(statements_js_1.ExistsQueryStatement, [query, negated]));
|
|
851
|
+
return this;
|
|
876
852
|
}
|
|
877
|
-
|
|
878
|
-
|
|
853
|
+
const rel = this._model.getRelationDescriptor(query);
|
|
854
|
+
if (!rel) {
|
|
855
|
+
throw new exceptions_js_1.OrmException(`relation ${query} not found in model ${this.constructor.name}`);
|
|
856
|
+
}
|
|
857
|
+
const handlers = this._container.resolve(Array.ofType(existsRelationHandlers_js_1.ExistsRelationHandler));
|
|
858
|
+
const handler = handlers.find((h) => h.Type === rel.Type);
|
|
859
|
+
if (!handler) {
|
|
860
|
+
throw new exceptions_js_1.OrmException(`no ExistsRelationHandler registered for relation type ${rel.Type} (relation ${query} on ${this.constructor.name})`);
|
|
861
|
+
}
|
|
862
|
+
const subquery = handler.apply(this, rel, query, callback);
|
|
863
|
+
if (subquery) {
|
|
864
|
+
this.pushStatement(this._container.resolve(statements_js_1.ExistsQueryStatement, [subquery, negated]));
|
|
879
865
|
}
|
|
880
866
|
return this;
|
|
881
867
|
}
|
|
882
868
|
whereBetween(column, val) {
|
|
883
|
-
this.
|
|
869
|
+
this.pushStatement(this._container.resolve(statements_js_1.BetweenStatement, [column, val, false, this.TableAlias]));
|
|
884
870
|
return this;
|
|
885
871
|
}
|
|
886
872
|
whereNotBetween(column, val) {
|
|
887
|
-
this.
|
|
873
|
+
this.pushStatement(this._container.resolve(statements_js_1.BetweenStatement, [column, val, true, this.TableAlias]));
|
|
888
874
|
return this;
|
|
889
875
|
}
|
|
890
876
|
whereInSet(column, val) {
|
|
891
|
-
this.
|
|
877
|
+
this.pushStatement(this._container.resolve(statements_js_1.InSetStatement, [column, val, false, this.TableAlias]));
|
|
892
878
|
return this;
|
|
893
879
|
}
|
|
894
880
|
whereNotInSet(column, val) {
|
|
895
|
-
this.
|
|
881
|
+
this.pushStatement(this._container.resolve(statements_js_1.InSetStatement, [column, val, true, this.TableAlias]));
|
|
896
882
|
return this;
|
|
897
883
|
}
|
|
898
884
|
clearWhere() {
|
|
@@ -932,10 +918,7 @@ let SelectQueryBuilder = SelectQueryBuilder_1 = class SelectQueryBuilder extends
|
|
|
932
918
|
this._distinct = false;
|
|
933
919
|
this._method = enums_js_1.QueryMethod.SELECT;
|
|
934
920
|
this._boolean = enums_js_1.WhereBoolean.AND;
|
|
935
|
-
this.
|
|
936
|
-
column: '',
|
|
937
|
-
order: enums_js_1.SortOrder.NONE,
|
|
938
|
-
};
|
|
921
|
+
this._sorts = [];
|
|
939
922
|
this._first = false;
|
|
940
923
|
this._limit = {
|
|
941
924
|
limit: -1,
|
|
@@ -950,13 +933,12 @@ let SelectQueryBuilder = SelectQueryBuilder_1 = class SelectQueryBuilder extends
|
|
|
950
933
|
return (await this);
|
|
951
934
|
}
|
|
952
935
|
setAlias(alias) {
|
|
953
|
-
|
|
954
|
-
|
|
955
|
-
|
|
956
|
-
this.
|
|
957
|
-
this.
|
|
958
|
-
this.
|
|
959
|
-
this._statements.forEach((c) => (c.TableAlias = alias));
|
|
936
|
+
// isNullOrWhitespace is not a type predicate; a false result guarantees a non-blank string
|
|
937
|
+
const resolvedAlias = (0, util_1.isNullOrWhitespace)(alias) ? `${this._driver.Options.AliasSeparator}${this._table}${this._driver.Options.AliasSeparator}` : alias;
|
|
938
|
+
this._tableAlias = resolvedAlias;
|
|
939
|
+
this._columns.forEach((c) => (c.TableAlias = resolvedAlias));
|
|
940
|
+
this._joinStatements.forEach((c) => (c.TableAlias = resolvedAlias));
|
|
941
|
+
this._statements.forEach((c) => (c.TableAlias = resolvedAlias));
|
|
960
942
|
return this;
|
|
961
943
|
}
|
|
962
944
|
clone() {
|
|
@@ -967,11 +949,18 @@ let SelectQueryBuilder = SelectQueryBuilder_1 = class SelectQueryBuilder extends
|
|
|
967
949
|
builder._joinStatements = this._joinStatements.map(c => c.clone(builder));
|
|
968
950
|
// Clone statements with mapped WhereBuilder references
|
|
969
951
|
builder._statements = this._statements.map(c => c.clone(builder));
|
|
952
|
+
// Clone group-by statements (previously dropped, silently losing GROUP BY
|
|
953
|
+
// on a cloned query — eg. orm-api count-pagination clones the builder).
|
|
954
|
+
builder._groupStatements = this._groupStatements.map(c => c.clone(builder));
|
|
955
|
+
// Carry relations and result middlewares over. These hold live objects and
|
|
956
|
+
// are shared the same way mergeRelations()/mergeBuilder() already share them.
|
|
957
|
+
builder._relations = [...this._relations];
|
|
958
|
+
builder._middlewares = [...this._middlewares];
|
|
970
959
|
/**
|
|
971
960
|
* ------------------------------------------------------------------
|
|
972
961
|
*/
|
|
973
962
|
builder._limit = { ...this._limit };
|
|
974
|
-
builder.
|
|
963
|
+
builder._sorts = this._sorts.map((s) => ({ ...s }));
|
|
975
964
|
builder._boolean = this._boolean;
|
|
976
965
|
builder._distinct = this._distinct;
|
|
977
966
|
builder._table = this._table;
|
|
@@ -1070,15 +1059,21 @@ let SelectQueryBuilder = SelectQueryBuilder_1 = class SelectQueryBuilder extends
|
|
|
1070
1059
|
this._getRelationInstance(relation).execute(callback);
|
|
1071
1060
|
return this;
|
|
1072
1061
|
}
|
|
1073
|
-
mergeBuilder(builder) {
|
|
1062
|
+
mergeBuilder(builder, includeStatements = true) {
|
|
1074
1063
|
this._columns = this._columns.concat(builder._columns);
|
|
1075
1064
|
this._cteStatement = builder._cteStatement;
|
|
1076
1065
|
this._distinct = builder._distinct;
|
|
1077
|
-
this
|
|
1078
|
-
|
|
1079
|
-
|
|
1080
|
-
|
|
1081
|
-
|
|
1066
|
+
// Fold the merged builder's sorts into this query's sorts (multi-column
|
|
1067
|
+
// ORDER BY). If the merged builder has no sorts, this keeps our own.
|
|
1068
|
+
this._sorts = this._sorts.concat(builder._sorts.map((s) => ({ ...s })));
|
|
1069
|
+
// `includeStatements: false` is used by JoinStatement so that a join
|
|
1070
|
+
// callback's WHERE conditions are NOT folded into the main query's WHERE
|
|
1071
|
+
// (which silently turns a LEFT JOIN into an inner filter). The join emits
|
|
1072
|
+
// those conditions in its own ON clause instead. Columns/sort are still
|
|
1073
|
+
// merged so join-callback selects (e.g. extra joined columns) keep working.
|
|
1074
|
+
if (includeStatements) {
|
|
1075
|
+
this.mergeStatements(builder);
|
|
1076
|
+
}
|
|
1082
1077
|
}
|
|
1083
1078
|
mergeRelations(builder) {
|
|
1084
1079
|
this._relations = this._relations.concat(builder._relations);
|
|
@@ -1089,6 +1084,21 @@ let SelectQueryBuilder = SelectQueryBuilder_1 = class SelectQueryBuilder extends
|
|
|
1089
1084
|
this._joinStatements = this._joinStatements.concat(builder._joinStatements);
|
|
1090
1085
|
this._statements = this._statements.concat(stms);
|
|
1091
1086
|
}
|
|
1087
|
+
/**
|
|
1088
|
+
* Includes soft-deleted rows in the result set by removing the default
|
|
1089
|
+
* `DeletedAt IS NULL` filter added by createQuery for @SoftDelete models.
|
|
1090
|
+
*/
|
|
1091
|
+
withDeleted() {
|
|
1092
|
+
const descriptor = (0, descriptor_js_1.extractModelDescriptor)(this._model);
|
|
1093
|
+
const deletedAt = descriptor?.SoftDelete?.DeletedAt;
|
|
1094
|
+
if (!deletedAt) {
|
|
1095
|
+
return this;
|
|
1096
|
+
}
|
|
1097
|
+
this._statements = this._statements.filter((s) => {
|
|
1098
|
+
return !(s instanceof statements_js_1.WhereStatement && s.Column === deletedAt && s.Operator === enums_js_1.SqlOperator.NULL);
|
|
1099
|
+
});
|
|
1100
|
+
return this;
|
|
1101
|
+
}
|
|
1092
1102
|
min(column, as) {
|
|
1093
1103
|
this._columns.push(this._container.resolve(statements_js_1.ColumnMethodStatement, [column, enums_js_1.ColumnMethods.MIN, as, this._tableAlias]));
|
|
1094
1104
|
return this;
|
|
@@ -1130,44 +1140,26 @@ let SelectQueryBuilder = SelectQueryBuilder_1 = class SelectQueryBuilder extends
|
|
|
1130
1140
|
return compiler.compile();
|
|
1131
1141
|
}
|
|
1132
1142
|
async all() {
|
|
1133
|
-
return await this;
|
|
1143
|
+
return await this.execute();
|
|
1134
1144
|
}
|
|
1135
1145
|
async resultExists() {
|
|
1136
|
-
|
|
1137
|
-
|
|
1138
|
-
|
|
1139
|
-
|
|
1140
|
-
|
|
1141
|
-
});
|
|
1142
|
-
}
|
|
1143
|
-
then(onfulfilled, onrejected) {
|
|
1144
|
-
this._queryMiddlewares.forEach((x) => x.beforeQueryExecution(this));
|
|
1145
|
-
return super.then((result) => {
|
|
1146
|
-
if (this._first) {
|
|
1147
|
-
if (Array.isArray(result)) {
|
|
1148
|
-
if (result.length !== 0) {
|
|
1149
|
-
return onfulfilled?.(result[0]);
|
|
1150
|
-
}
|
|
1151
|
-
else {
|
|
1152
|
-
try {
|
|
1153
|
-
return onfulfilled?.(undefined);
|
|
1154
|
-
}
|
|
1155
|
-
catch (err) {
|
|
1156
|
-
onrejected?.(err);
|
|
1157
|
-
}
|
|
1158
|
-
}
|
|
1159
|
-
}
|
|
1160
|
-
else {
|
|
1161
|
-
return onfulfilled?.(result);
|
|
1162
|
-
}
|
|
1163
|
-
}
|
|
1164
|
-
else {
|
|
1165
|
-
return onfulfilled?.(result);
|
|
1166
|
-
}
|
|
1167
|
-
}, onrejected);
|
|
1146
|
+
const res = await this.execute();
|
|
1147
|
+
if (Array.isArray(res)) {
|
|
1148
|
+
return res.length > 0;
|
|
1149
|
+
}
|
|
1150
|
+
return res !== undefined && res !== null;
|
|
1168
1151
|
}
|
|
1169
|
-
|
|
1170
|
-
|
|
1152
|
+
/**
|
|
1153
|
+
* Overrides the engine rather than `execute()` so that the `takeFirst()` unwrapping
|
|
1154
|
+
* happens *inside* the memo. `beforeQueryExecution` is dispatched by `Builder._run()`
|
|
1155
|
+
* for every builder type, so it still fires once per builder rather than once per await.
|
|
1156
|
+
*/
|
|
1157
|
+
async _run() {
|
|
1158
|
+
const result = await super._run();
|
|
1159
|
+
if (this._first && Array.isArray(result)) {
|
|
1160
|
+
return (result.length !== 0 ? result[0] : undefined);
|
|
1161
|
+
}
|
|
1162
|
+
return result;
|
|
1171
1163
|
}
|
|
1172
1164
|
};
|
|
1173
1165
|
exports.SelectQueryBuilder = SelectQueryBuilder;
|
|
@@ -1193,6 +1185,10 @@ let DeleteQueryBuilder = class DeleteQueryBuilder extends QueryBuilder {
|
|
|
1193
1185
|
offset: -1,
|
|
1194
1186
|
};
|
|
1195
1187
|
this.QueryContext = interfaces_js_1.QueryContext.Delete;
|
|
1188
|
+
// Query middlewares (e.g. rbac ownership enforcement) must run for deletes
|
|
1189
|
+
// too, not only selects — otherwise :own permission constraints are never
|
|
1190
|
+
// applied and any row can be deleted.
|
|
1191
|
+
this._queryMiddlewares.forEach((x) => x.afterQueryCreation(this));
|
|
1196
1192
|
}
|
|
1197
1193
|
toDB() {
|
|
1198
1194
|
return this._container.resolve(interfaces_js_1.DeleteQueryCompiler, [this]).compile();
|
|
@@ -1232,6 +1228,9 @@ let OnDuplicateQueryBuilder = class OnDuplicateQueryBuilder {
|
|
|
1232
1228
|
this._columnsToUpdate = columns;
|
|
1233
1229
|
return this;
|
|
1234
1230
|
}
|
|
1231
|
+
execute() {
|
|
1232
|
+
return this._parent.execute();
|
|
1233
|
+
}
|
|
1235
1234
|
then(onfulfilled, onrejected) {
|
|
1236
1235
|
return this._parent.then(onfulfilled, onrejected);
|
|
1237
1236
|
}
|
|
@@ -1255,6 +1254,10 @@ let UpdateQueryBuilder = class UpdateQueryBuilder extends QueryBuilder {
|
|
|
1255
1254
|
this._boolean = enums_js_1.WhereBoolean.AND;
|
|
1256
1255
|
this._statements = [];
|
|
1257
1256
|
this.QueryContext = interfaces_js_1.QueryContext.Update;
|
|
1257
|
+
// Query middlewares (e.g. rbac ownership enforcement) must run for updates
|
|
1258
|
+
// too, not only selects — otherwise :own permission constraints are never
|
|
1259
|
+
// applied and any row can be updated.
|
|
1260
|
+
this._queryMiddlewares.forEach((x) => x.afterQueryCreation(this));
|
|
1258
1261
|
}
|
|
1259
1262
|
in(name) {
|
|
1260
1263
|
this.setTable(name);
|
|
@@ -1290,6 +1293,10 @@ let InsertQueryBuilder = class InsertQueryBuilder extends QueryBuilder {
|
|
|
1290
1293
|
get Replace() {
|
|
1291
1294
|
return this._replace;
|
|
1292
1295
|
}
|
|
1296
|
+
/** Columns requested via {@link returning}. Empty when no RETURNING clause was asked for. */
|
|
1297
|
+
get Returning() {
|
|
1298
|
+
return this._returning ?? [];
|
|
1299
|
+
}
|
|
1293
1300
|
constructor(container, driver, model) {
|
|
1294
1301
|
super(container, driver, model);
|
|
1295
1302
|
this._update = false;
|
|
@@ -1298,6 +1305,33 @@ let InsertQueryBuilder = class InsertQueryBuilder extends QueryBuilder {
|
|
|
1298
1305
|
this._columns = [];
|
|
1299
1306
|
this._values = [];
|
|
1300
1307
|
this.QueryContext = interfaces_js_1.QueryContext.Insert;
|
|
1308
|
+
// Inserts get the same middleware pass as the other three builders. Note that a
|
|
1309
|
+
// middleware which needs to see or amend the row payload must use
|
|
1310
|
+
// `beforeQueryExecution` instead — at construction time `values()` has not been called.
|
|
1311
|
+
this._queryMiddlewares.forEach((x) => x.afterQueryCreation(this));
|
|
1312
|
+
}
|
|
1313
|
+
/**
|
|
1314
|
+
* Forces `column` to `value` on every row of the payload, overwriting whatever the caller
|
|
1315
|
+
* supplied.
|
|
1316
|
+
*
|
|
1317
|
+
* This is the write path for a policy that must not be negotiable — rbac's `createOwn`
|
|
1318
|
+
* ownership stamp. `values()` cannot serve: it appends rows and takes the column list from
|
|
1319
|
+
* its own argument, so calling it a second time adds a row rather than amending the
|
|
1320
|
+
* existing ones.
|
|
1321
|
+
*
|
|
1322
|
+
* @param column - column to overwrite on every row
|
|
1323
|
+
* @param value - value to force
|
|
1324
|
+
*/
|
|
1325
|
+
forceColumn(column, value) {
|
|
1326
|
+
const existing = this._columns.findIndex((c) => !(c.Column instanceof RawQuery) && c.Column === column);
|
|
1327
|
+
if (existing === -1) {
|
|
1328
|
+
this.columns([...this._columns.filter((c) => !(c.Column instanceof RawQuery)).map((c) => c.Column), column]);
|
|
1329
|
+
this._values.forEach((row) => row.push(value));
|
|
1330
|
+
return this;
|
|
1331
|
+
}
|
|
1332
|
+
// eslint-disable-next-line security/detect-object-injection
|
|
1333
|
+
this._values.forEach((row) => (row[existing] = value));
|
|
1334
|
+
return this;
|
|
1301
1335
|
}
|
|
1302
1336
|
/**
|
|
1303
1337
|
* Sets insert to ignore on duplicate
|
|
@@ -1310,8 +1344,21 @@ let InsertQueryBuilder = class InsertQueryBuilder extends QueryBuilder {
|
|
|
1310
1344
|
this._replace = true;
|
|
1311
1345
|
return this;
|
|
1312
1346
|
}
|
|
1347
|
+
/**
|
|
1348
|
+
* Asks the dialect to echo the given columns of every inserted row back.
|
|
1349
|
+
*
|
|
1350
|
+
* @throws NotSupported on drivers whose `supportedFeatures().insertReturning` is false —
|
|
1351
|
+
* silently doing nothing is how this API was a no-op on MySQL and MSSQL for years.
|
|
1352
|
+
*/
|
|
1313
1353
|
returning(columns) {
|
|
1354
|
+
if (!this.Driver.supportedFeatures().insertReturning) {
|
|
1355
|
+
throw new exceptions_1.NotSupported(`driver ${this.Driver.Options.Driver} does not support RETURNING on INSERT`);
|
|
1356
|
+
}
|
|
1314
1357
|
this._returning = columns;
|
|
1358
|
+
// onDuplicate() sets Upsert unconditionally and wins if it runs afterwards.
|
|
1359
|
+
if (this.QueryContext === interfaces_js_1.QueryContext.Insert) {
|
|
1360
|
+
this.QueryContext = interfaces_js_1.QueryContext.InsertReturning;
|
|
1361
|
+
}
|
|
1315
1362
|
return this;
|
|
1316
1363
|
}
|
|
1317
1364
|
values(data) {
|
|
@@ -1473,6 +1520,7 @@ let ColumnQueryBuilder = class ColumnQueryBuilder {
|
|
|
1473
1520
|
this.Comment = '';
|
|
1474
1521
|
this.Unique = false;
|
|
1475
1522
|
this.Unsigned = false;
|
|
1523
|
+
this.InlinePrimaryKey = true;
|
|
1476
1524
|
this.Args.push(...args);
|
|
1477
1525
|
}
|
|
1478
1526
|
notNull() {
|
|
@@ -1643,10 +1691,11 @@ exports.AlterTableQueryBuilder = AlterTableQueryBuilder = __decorate([
|
|
|
1643
1691
|
], AlterTableQueryBuilder);
|
|
1644
1692
|
let TableQueryBuilder = class TableQueryBuilder extends QueryBuilder {
|
|
1645
1693
|
/**
|
|
1646
|
-
* Alias for
|
|
1694
|
+
* Alias for binary(name, 16 ) - uuids are stored as 16-byte BINARY to match
|
|
1695
|
+
* the UuidConverter ( which writes a dashed uuid as a 16-byte buffer ).
|
|
1647
1696
|
*/
|
|
1648
1697
|
uuid(name) {
|
|
1649
|
-
return this.
|
|
1698
|
+
return this.binary(name, 16);
|
|
1650
1699
|
}
|
|
1651
1700
|
ifExists() {
|
|
1652
1701
|
this._checkExists = true;
|
|
@@ -2053,6 +2102,16 @@ function createQuery(model, query, injectModel = true) {
|
|
|
2053
2102
|
}
|
|
2054
2103
|
qr.middleware(new discrimination_middleware_js_1.DiscriminationMapMiddleware(dsc));
|
|
2055
2104
|
qr.setTable(dsc.TableName);
|
|
2105
|
+
// Soft-delete read filtering: by default exclude rows that have been soft
|
|
2106
|
+
// deleted (DeletedAt IS NOT NULL). SelectQueryBuilder.withDeleted() removes
|
|
2107
|
+
// this default statement to include soft-deleted rows again.
|
|
2108
|
+
// Guarded on the DeletedAt column actually being present in the model's
|
|
2109
|
+
// reflected columns — a filter on a column the schema does not expose is
|
|
2110
|
+
// impossible anyway, and the guard keeps queries working when table info
|
|
2111
|
+
// has not (yet) surfaced the column.
|
|
2112
|
+
if (qr instanceof SelectQueryBuilder && dsc.SoftDelete?.DeletedAt && dsc.Columns?.some((c) => c.Name === dsc.SoftDelete.DeletedAt)) {
|
|
2113
|
+
qr.whereNull(dsc.SoftDelete.DeletedAt);
|
|
2114
|
+
}
|
|
2056
2115
|
if (driver.Options.Database) {
|
|
2057
2116
|
qr.database(driver.Options.Database);
|
|
2058
2117
|
}
|