@spinajs/orm 2.0.481 → 2.0.484
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 +127 -11
- package/lib/cjs/builders.d.ts.map +1 -1
- package/lib/cjs/builders.js +368 -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 +138 -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 +300 -10
- 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 +127 -11
- package/lib/mjs/builders.d.ts.map +1 -1
- package/lib/mjs/builders.js +370 -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 +134 -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 +300 -10
- 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 +10 -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,104 @@ 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
|
-
|
|
98
|
-
|
|
99
|
-
|
|
100
|
-
|
|
101
|
-
|
|
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
|
+
//
|
|
77
|
+
// Awaited in sequence, not `forEach`ed: a hook that needs a database round-trip to
|
|
78
|
+
// decide — an rbac rule whose ownership lives in another table — could otherwise only
|
|
79
|
+
// return a promise into the void, and the query would execute anyway. For a security
|
|
80
|
+
// check that is the difference between enforcing and pretending to.
|
|
81
|
+
for (const middleware of this._queryMiddlewares) {
|
|
82
|
+
await middleware.beforeQueryExecution(this);
|
|
83
|
+
}
|
|
84
|
+
const result = (await this._driver.execute(this));
|
|
85
|
+
// Snapshot the pipeline once, *after* the driver call: compiling the query is what
|
|
86
|
+
// registers the relation middlewares (the driver calls `toDB()`), so this is the first
|
|
87
|
+
// point at which the list is complete. Everything below runs against this immutable copy,
|
|
88
|
+
// so a middleware registered later cannot change the pipeline mid-flight, and — crucially —
|
|
89
|
+
// we never call `Array.prototype.reverse()` on `this._middlewares`, which mutates in place
|
|
90
|
+
// and used to flip `modelCreation` resolution order on every execution ( B8 ).
|
|
91
|
+
const middlewares = [...this._middlewares];
|
|
92
|
+
const creationOrder = [...middlewares].reverse();
|
|
93
|
+
if (this._asRaw) {
|
|
94
|
+
return result;
|
|
95
|
+
}
|
|
96
|
+
const transformedResult = result;
|
|
97
|
+
// if we have something to transform ...
|
|
98
|
+
if (transformedResult) {
|
|
99
|
+
middlewares.forEach((m) => {
|
|
100
|
+
Object.assign(transformedResult, m.afterQuery(transformedResult));
|
|
101
|
+
});
|
|
102
|
+
}
|
|
103
|
+
if (this._model && !this._nonSelect) {
|
|
104
|
+
// TODO: rething this casting
|
|
105
|
+
const models = transformedResult.map((r) => {
|
|
106
|
+
let model = null;
|
|
107
|
+
for (const middleware of creationOrder) {
|
|
108
|
+
model = middleware.modelCreation(r);
|
|
109
|
+
if (model !== null) {
|
|
110
|
+
break;
|
|
108
111
|
}
|
|
109
112
|
}
|
|
110
|
-
|
|
111
|
-
|
|
113
|
+
if (model === null) {
|
|
114
|
+
model = di_1.DI.resolve('__orm_model_factory__', [this._model]);
|
|
112
115
|
}
|
|
113
|
-
|
|
114
|
-
|
|
115
|
-
|
|
116
|
-
|
|
117
|
-
|
|
118
|
-
|
|
119
|
-
|
|
116
|
+
model.hydrate(r);
|
|
117
|
+
model.IsDirty = false;
|
|
118
|
+
// This is the one point at which the instance's columns hold exactly what the
|
|
119
|
+
// database returned, so it is the diff baseline for `save()`. Relation members
|
|
120
|
+
// are attached later by the afterHydration middlewares below, which record their
|
|
121
|
+
// own member keys into this same snapshot.
|
|
122
|
+
model.takeSnapshot();
|
|
123
|
+
// Nested relation data that arrived on the row itself ( belongsTo LEFT JOIN,
|
|
124
|
+
// and hasMany arrays passed straight to hydrate ) was attached by the hydrators
|
|
125
|
+
// inside `hydrate()` above — i.e. before the snapshot existed, so their own
|
|
126
|
+
// `snapshotRelation` calls no-opped. Record those relations now.
|
|
127
|
+
for (const name of (model.ModelDescriptor?.Relations ?? new Map()).keys()) {
|
|
128
|
+
if (model[name]?.Populated) {
|
|
129
|
+
model.snapshotRelation(name);
|
|
130
|
+
}
|
|
120
131
|
}
|
|
132
|
+
return model;
|
|
133
|
+
});
|
|
134
|
+
if (middlewares.length > 0) {
|
|
135
|
+
await Promise.all(middlewares.map((m) => m.afterHydration(models)));
|
|
121
136
|
}
|
|
122
|
-
|
|
123
|
-
|
|
124
|
-
|
|
125
|
-
|
|
126
|
-
|
|
127
|
-
|
|
128
|
-
|
|
129
|
-
|
|
130
|
-
|
|
137
|
+
return models;
|
|
138
|
+
}
|
|
139
|
+
return transformedResult;
|
|
140
|
+
}
|
|
141
|
+
/**
|
|
142
|
+
* Executes the query. The single entry point for execution — `then()` delegates here.
|
|
143
|
+
* The underlying work runs exactly once per builder instance; subsequent calls resolve
|
|
144
|
+
* with the memoized result.
|
|
145
|
+
*/
|
|
146
|
+
execute() {
|
|
147
|
+
if (!this._executionPromise) {
|
|
148
|
+
this._executionPromise = this._run();
|
|
149
|
+
}
|
|
150
|
+
return this._executionPromise;
|
|
151
|
+
}
|
|
152
|
+
then(onfulfilled, onrejected) {
|
|
153
|
+
return this.execute().then(onfulfilled, onrejected);
|
|
154
|
+
}
|
|
155
|
+
catch(onrejected) {
|
|
156
|
+
return this.execute().catch(onrejected);
|
|
157
|
+
}
|
|
158
|
+
finally(onfinally) {
|
|
159
|
+
return this.execute().finally(onfinally);
|
|
131
160
|
}
|
|
132
161
|
middleware(middleware) {
|
|
133
162
|
this._middlewares.push(middleware);
|
|
@@ -199,7 +228,7 @@ let QueryBuilder = class QueryBuilder extends Builder {
|
|
|
199
228
|
*
|
|
200
229
|
*/
|
|
201
230
|
setTable(table, alias) {
|
|
202
|
-
if (
|
|
231
|
+
if ((0, util_1.isNullOrWhitespace)(table)) {
|
|
203
232
|
throw new exceptions_1.InvalidArgument('table name is empty');
|
|
204
233
|
}
|
|
205
234
|
this._table = table;
|
|
@@ -236,9 +265,7 @@ let LimitBuilder = class LimitBuilder {
|
|
|
236
265
|
};
|
|
237
266
|
}
|
|
238
267
|
take(count) {
|
|
239
|
-
|
|
240
|
-
throw new exceptions_1.InvalidArgument(`take count cannot be negative number`);
|
|
241
|
-
}
|
|
268
|
+
(0, util_1._check_arg)((0, util_1._positive)())(count, 'take count');
|
|
242
269
|
this._limit.limit = count;
|
|
243
270
|
return this;
|
|
244
271
|
}
|
|
@@ -264,7 +291,7 @@ let LimitBuilder = class LimitBuilder {
|
|
|
264
291
|
const result = (await this);
|
|
265
292
|
if (result === undefined || (Array.isArray(result) && result.length === 0)) {
|
|
266
293
|
if (typeof error === 'function') {
|
|
267
|
-
|
|
294
|
+
throw error(this.toDB());
|
|
268
295
|
}
|
|
269
296
|
else
|
|
270
297
|
throw error;
|
|
@@ -293,10 +320,7 @@ exports.LimitBuilder = LimitBuilder = __decorate([
|
|
|
293
320
|
], LimitBuilder);
|
|
294
321
|
let OrderByBuilder = class OrderByBuilder {
|
|
295
322
|
constructor() {
|
|
296
|
-
this.
|
|
297
|
-
column: '',
|
|
298
|
-
order: enums_js_1.SortOrder.ASC,
|
|
299
|
-
};
|
|
323
|
+
this._sorts = [];
|
|
300
324
|
}
|
|
301
325
|
order(column, direction) {
|
|
302
326
|
if (!column) {
|
|
@@ -309,10 +333,10 @@ let OrderByBuilder = class OrderByBuilder {
|
|
|
309
333
|
});
|
|
310
334
|
return this;
|
|
311
335
|
}
|
|
312
|
-
this.
|
|
336
|
+
this._sorts.push({
|
|
313
337
|
column,
|
|
314
338
|
order: direction,
|
|
315
|
-
};
|
|
339
|
+
});
|
|
316
340
|
return this;
|
|
317
341
|
}
|
|
318
342
|
orderBy(column) {
|
|
@@ -323,10 +347,10 @@ let OrderByBuilder = class OrderByBuilder {
|
|
|
323
347
|
});
|
|
324
348
|
return this;
|
|
325
349
|
}
|
|
326
|
-
this.
|
|
350
|
+
this._sorts.push({
|
|
327
351
|
column,
|
|
328
352
|
order: enums_js_1.SortOrder.ASC,
|
|
329
|
-
};
|
|
353
|
+
});
|
|
330
354
|
return this;
|
|
331
355
|
}
|
|
332
356
|
orderByDescending(column) {
|
|
@@ -337,14 +361,25 @@ let OrderByBuilder = class OrderByBuilder {
|
|
|
337
361
|
});
|
|
338
362
|
return this;
|
|
339
363
|
}
|
|
340
|
-
this.
|
|
364
|
+
this._sorts.push({
|
|
341
365
|
column,
|
|
342
366
|
order: enums_js_1.SortOrder.DESC,
|
|
343
|
-
};
|
|
367
|
+
});
|
|
344
368
|
return this;
|
|
345
369
|
}
|
|
370
|
+
/**
|
|
371
|
+
* Returns the FIRST sort entry (or null) for backward compat with dialect
|
|
372
|
+
* packages that emit a single ORDER BY column. Use getSorts() for all entries.
|
|
373
|
+
*/
|
|
346
374
|
getSort() {
|
|
347
|
-
|
|
375
|
+
const sort = this._sorts.find((s) => s.column.trim() !== '');
|
|
376
|
+
return sort ?? null;
|
|
377
|
+
}
|
|
378
|
+
/**
|
|
379
|
+
* Returns all sort entries (multi-column ORDER BY), skipping empty columns.
|
|
380
|
+
*/
|
|
381
|
+
getSorts() {
|
|
382
|
+
return this._sorts.filter((s) => s.column.trim() !== '');
|
|
348
383
|
}
|
|
349
384
|
};
|
|
350
385
|
exports.OrderByBuilder = OrderByBuilder;
|
|
@@ -556,6 +591,23 @@ let WithRecursiveBuilder = class WithRecursiveBuilder {
|
|
|
556
591
|
this._cteStatement = this._container.resolve(statements_js_1.WithRecursiveStatement, ['cte', this, rcKeyName, pkName]);
|
|
557
592
|
return this;
|
|
558
593
|
}
|
|
594
|
+
/**
|
|
595
|
+
* Drops the recursive CTE from this builder.
|
|
596
|
+
*
|
|
597
|
+
* `WithRecursiveStatement.build()` compiles two CLONES of the owning query — the anchor
|
|
598
|
+
* member and the recursive member of the CTE. `clone()` copies `_cteStatement`, so each
|
|
599
|
+
* clone was still marked recursive and compiling it re-entered `build()`, which cloned
|
|
600
|
+
* again: an unbounded mutual recursion between `toDB()` and the recursive compiler that
|
|
601
|
+
* ended in a stack overflow rather than a query. The two member queries are by definition
|
|
602
|
+
* not themselves recursive, so the statement clears the flag on its clones.
|
|
603
|
+
*
|
|
604
|
+
* Named alongside `clearJoins()` / `clearWhere()`, which `build()` already uses to strip the
|
|
605
|
+
* parts of the parent query each member must not inherit.
|
|
606
|
+
*/
|
|
607
|
+
clearRecursive() {
|
|
608
|
+
this._cteStatement = undefined;
|
|
609
|
+
return this;
|
|
610
|
+
}
|
|
559
611
|
};
|
|
560
612
|
exports.WithRecursiveBuilder = WithRecursiveBuilder;
|
|
561
613
|
exports.WithRecursiveBuilder = WithRecursiveBuilder = __decorate([
|
|
@@ -590,12 +642,31 @@ let WhereBuilder = WhereBuilder_1 = class WhereBuilder {
|
|
|
590
642
|
clone(_parent) {
|
|
591
643
|
// TODO: fix this cast
|
|
592
644
|
const builder = new WhereBuilder_1(_parent);
|
|
593
|
-
builder._statements = this._statements.map((s) =>
|
|
645
|
+
builder._statements = this._statements.map((s) => {
|
|
646
|
+
const cloned = s.clone(builder);
|
|
647
|
+
// preserve the per-statement boolean connector (clone() rebuilds the
|
|
648
|
+
// statement from scratch and would otherwise reset it to the AND default)
|
|
649
|
+
cloned.Boolean = s.Boolean;
|
|
650
|
+
return cloned;
|
|
651
|
+
});
|
|
594
652
|
builder._boolean = this._boolean;
|
|
595
653
|
builder._model = this._model;
|
|
596
654
|
builder._tableAlias = this.TableAlias;
|
|
597
655
|
return builder;
|
|
598
656
|
}
|
|
657
|
+
/**
|
|
658
|
+
* Pushes a statement onto this builder, stamping it with the currently pending
|
|
659
|
+
* boolean connector (set by {@link orWhere}/{@link andWhere}). The pending
|
|
660
|
+
* connector applies to the NEXT pushed statement only and resets to AND
|
|
661
|
+
* afterwards, so `where(a).where(b).orWhere(c).where(d)` compiles to
|
|
662
|
+
* `a AND b OR c AND d` rather than rewriting the whole clause.
|
|
663
|
+
*/
|
|
664
|
+
pushStatement(statement) {
|
|
665
|
+
statement.Boolean = this._boolean;
|
|
666
|
+
this._statements.push(statement);
|
|
667
|
+
this._boolean = enums_js_1.WhereBoolean.AND;
|
|
668
|
+
return this;
|
|
669
|
+
}
|
|
599
670
|
when(condition, callback, callbackElse) {
|
|
600
671
|
if (condition) {
|
|
601
672
|
if (callback)
|
|
@@ -640,18 +711,18 @@ let WhereBuilder = WhereBuilder_1 = class WhereBuilder {
|
|
|
640
711
|
return this.where(RawQuery.create(column ? 'TRUE' : 'FALSE'));
|
|
641
712
|
}
|
|
642
713
|
if (column instanceof RawQuery) {
|
|
643
|
-
this.
|
|
714
|
+
this.pushStatement(this._container.resolve(statements_js_1.RawQueryStatement, [column.Query, column.Bindings, self.TableAlias]));
|
|
644
715
|
return this;
|
|
645
716
|
}
|
|
646
717
|
// handle nested where's
|
|
647
718
|
if (lodash_1.default.isFunction(column)) {
|
|
648
719
|
const builder = new WhereBuilder_1(this);
|
|
649
720
|
column.call(builder);
|
|
650
|
-
self.
|
|
721
|
+
self.pushStatement(this._container.resolve(statements_js_1.WhereQueryStatement, [builder, self.TableAlias]));
|
|
651
722
|
return this;
|
|
652
723
|
}
|
|
653
724
|
if (column instanceof util_1.Lazy) {
|
|
654
|
-
this.
|
|
725
|
+
this.pushStatement(this._container.resolve(statements_js_1.LazyQueryStatement, [column, this]));
|
|
655
726
|
return this;
|
|
656
727
|
}
|
|
657
728
|
// handle simple key = object[key] AND ....
|
|
@@ -680,7 +751,7 @@ let WhereBuilder = WhereBuilder_1 = class WhereBuilder {
|
|
|
680
751
|
if (sVal === null) {
|
|
681
752
|
return this.whereNull(c);
|
|
682
753
|
}
|
|
683
|
-
self.
|
|
754
|
+
self.pushStatement(self._container.resolve(statements_js_1.WhereStatement, [c, enums_js_1.SqlOperator.EQ, sVal, this]));
|
|
684
755
|
return self;
|
|
685
756
|
}
|
|
686
757
|
/**
|
|
@@ -699,12 +770,17 @@ let WhereBuilder = WhereBuilder_1 = class WhereBuilder {
|
|
|
699
770
|
throw new exceptions_1.InvalidArgument(`value cannot be NaN`);
|
|
700
771
|
}
|
|
701
772
|
if (sVal === null) {
|
|
702
|
-
|
|
703
|
-
|
|
704
|
-
|
|
705
|
-
|
|
773
|
+
const op = String(o).toLowerCase();
|
|
774
|
+
// where(col, '=', null) => IS NULL ; where(col, '!=' / '<>', null) => IS NOT NULL
|
|
775
|
+
if (op === enums_js_1.SqlOperator.EQ) {
|
|
776
|
+
return this.whereNull(c);
|
|
777
|
+
}
|
|
778
|
+
if (op === enums_js_1.SqlOperator.NOT || op === enums_js_1.SqlOperator.NOT_2) {
|
|
779
|
+
return this.whereNotNull(c);
|
|
780
|
+
}
|
|
781
|
+
throw new exceptions_1.InvalidArgument(`operator ${o} cannot be used with null value ( only =, !=, <> are allowed )`);
|
|
706
782
|
}
|
|
707
|
-
self.
|
|
783
|
+
self.pushStatement(self._container.resolve(statements_js_1.WhereStatement, [c, o, sVal, self]));
|
|
708
784
|
return this;
|
|
709
785
|
}
|
|
710
786
|
}
|
|
@@ -721,9 +797,9 @@ let WhereBuilder = WhereBuilder_1 = class WhereBuilder {
|
|
|
721
797
|
for (const key of Object.keys(obj).filter((x) => obj[x] !== undefined)) {
|
|
722
798
|
const val = obj[key];
|
|
723
799
|
if (Array.isArray(val)) {
|
|
724
|
-
|
|
725
|
-
|
|
726
|
-
|
|
800
|
+
// empty array => SQL `IN ()` semantics => match nothing (FALSE),
|
|
801
|
+
// never "no condition" (which would match everything)
|
|
802
|
+
this.whereIn(key, val);
|
|
727
803
|
}
|
|
728
804
|
else if (val === null) {
|
|
729
805
|
this.whereNull(key);
|
|
@@ -734,165 +810,82 @@ let WhereBuilder = WhereBuilder_1 = class WhereBuilder {
|
|
|
734
810
|
return this;
|
|
735
811
|
}
|
|
736
812
|
whereNotNull(column) {
|
|
737
|
-
this.
|
|
813
|
+
this.pushStatement(this._container.resolve(statements_js_1.WhereStatement, [column, enums_js_1.SqlOperator.NOT_NULL, null, this]));
|
|
738
814
|
return this;
|
|
739
815
|
}
|
|
740
816
|
whereNull(column) {
|
|
741
|
-
this.
|
|
817
|
+
this.pushStatement(this._container.resolve(statements_js_1.WhereStatement, [column, enums_js_1.SqlOperator.NULL, null, this]));
|
|
742
818
|
return this;
|
|
743
819
|
}
|
|
744
820
|
whereNot(column, val) {
|
|
745
821
|
return this.where(column, enums_js_1.SqlOperator.NOT, val);
|
|
746
822
|
}
|
|
747
823
|
whereIn(column, val) {
|
|
748
|
-
|
|
824
|
+
// `IN ()` matches nothing in SQL; compile an empty set to FALSE rather than
|
|
825
|
+
// emitting no condition (which would silently match every row).
|
|
826
|
+
if (Array.isArray(val) && val.length === 0) {
|
|
827
|
+
this.where(false);
|
|
828
|
+
return this;
|
|
829
|
+
}
|
|
830
|
+
this.pushStatement(this._container.resolve(statements_js_1.InStatement, [column, val, false, this]));
|
|
749
831
|
return this;
|
|
750
832
|
}
|
|
751
833
|
whereNotIn(column, val) {
|
|
752
|
-
this.
|
|
834
|
+
this.pushStatement(this._container.resolve(statements_js_1.InStatement, [column, val, true, this]));
|
|
753
835
|
return this;
|
|
754
836
|
}
|
|
755
837
|
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;
|
|
838
|
+
return this.buildExistsClause(query, false, callback);
|
|
819
839
|
}
|
|
820
840
|
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
|
-
}
|
|
841
|
+
return this.buildExistsClause(query, true, callback);
|
|
842
|
+
}
|
|
843
|
+
/**
|
|
844
|
+
* Shared implementation for {@link whereExist} / {@link whereNotExists}.
|
|
845
|
+
*
|
|
846
|
+
* For a ready sub-query it pushes an {@link ExistsQueryStatement} directly. For a relation
|
|
847
|
+
* name it resolves the matching {@link ExistsRelationHandler} from the container (one per
|
|
848
|
+
* {@link RelationType}) and lets it either mutate this builder or return a correlated
|
|
849
|
+
* sub-query that we then wrap in EXISTS / NOT EXISTS.
|
|
850
|
+
*
|
|
851
|
+
* @param query relation name or a ready sub-query
|
|
852
|
+
* @param negated `true` for NOT EXISTS, `false` for EXISTS
|
|
853
|
+
* @param callback optional where-callback applied to the relation sub-query
|
|
854
|
+
*/
|
|
855
|
+
buildExistsClause(query, negated, callback) {
|
|
856
|
+
if (typeof query !== 'string') {
|
|
857
|
+
this.pushStatement(this._container.resolve(statements_js_1.ExistsQueryStatement, [query, negated]));
|
|
858
|
+
return this;
|
|
876
859
|
}
|
|
877
|
-
|
|
878
|
-
|
|
860
|
+
const rel = this._model.getRelationDescriptor(query);
|
|
861
|
+
if (!rel) {
|
|
862
|
+
throw new exceptions_js_1.OrmException(`relation ${query} not found in model ${this.constructor.name}`);
|
|
863
|
+
}
|
|
864
|
+
const handlers = this._container.resolve(Array.ofType(existsRelationHandlers_js_1.ExistsRelationHandler));
|
|
865
|
+
const handler = handlers.find((h) => h.Type === rel.Type);
|
|
866
|
+
if (!handler) {
|
|
867
|
+
throw new exceptions_js_1.OrmException(`no ExistsRelationHandler registered for relation type ${rel.Type} (relation ${query} on ${this.constructor.name})`);
|
|
868
|
+
}
|
|
869
|
+
const subquery = handler.apply(this, rel, query, callback);
|
|
870
|
+
if (subquery) {
|
|
871
|
+
this.pushStatement(this._container.resolve(statements_js_1.ExistsQueryStatement, [subquery, negated]));
|
|
879
872
|
}
|
|
880
873
|
return this;
|
|
881
874
|
}
|
|
882
875
|
whereBetween(column, val) {
|
|
883
|
-
this.
|
|
876
|
+
this.pushStatement(this._container.resolve(statements_js_1.BetweenStatement, [column, val, false, this.TableAlias]));
|
|
884
877
|
return this;
|
|
885
878
|
}
|
|
886
879
|
whereNotBetween(column, val) {
|
|
887
|
-
this.
|
|
880
|
+
this.pushStatement(this._container.resolve(statements_js_1.BetweenStatement, [column, val, true, this.TableAlias]));
|
|
888
881
|
return this;
|
|
889
882
|
}
|
|
890
883
|
whereInSet(column, val) {
|
|
891
|
-
this.
|
|
884
|
+
this.pushStatement(this._container.resolve(statements_js_1.InSetStatement, [column, val, false, this.TableAlias]));
|
|
892
885
|
return this;
|
|
893
886
|
}
|
|
894
887
|
whereNotInSet(column, val) {
|
|
895
|
-
this.
|
|
888
|
+
this.pushStatement(this._container.resolve(statements_js_1.InSetStatement, [column, val, true, this.TableAlias]));
|
|
896
889
|
return this;
|
|
897
890
|
}
|
|
898
891
|
clearWhere() {
|
|
@@ -932,10 +925,7 @@ let SelectQueryBuilder = SelectQueryBuilder_1 = class SelectQueryBuilder extends
|
|
|
932
925
|
this._distinct = false;
|
|
933
926
|
this._method = enums_js_1.QueryMethod.SELECT;
|
|
934
927
|
this._boolean = enums_js_1.WhereBoolean.AND;
|
|
935
|
-
this.
|
|
936
|
-
column: '',
|
|
937
|
-
order: enums_js_1.SortOrder.NONE,
|
|
938
|
-
};
|
|
928
|
+
this._sorts = [];
|
|
939
929
|
this._first = false;
|
|
940
930
|
this._limit = {
|
|
941
931
|
limit: -1,
|
|
@@ -950,13 +940,12 @@ let SelectQueryBuilder = SelectQueryBuilder_1 = class SelectQueryBuilder extends
|
|
|
950
940
|
return (await this);
|
|
951
941
|
}
|
|
952
942
|
setAlias(alias) {
|
|
953
|
-
|
|
954
|
-
|
|
955
|
-
|
|
956
|
-
this.
|
|
957
|
-
this.
|
|
958
|
-
this.
|
|
959
|
-
this._statements.forEach((c) => (c.TableAlias = alias));
|
|
943
|
+
// isNullOrWhitespace is not a type predicate; a false result guarantees a non-blank string
|
|
944
|
+
const resolvedAlias = (0, util_1.isNullOrWhitespace)(alias) ? `${this._driver.Options.AliasSeparator}${this._table}${this._driver.Options.AliasSeparator}` : alias;
|
|
945
|
+
this._tableAlias = resolvedAlias;
|
|
946
|
+
this._columns.forEach((c) => (c.TableAlias = resolvedAlias));
|
|
947
|
+
this._joinStatements.forEach((c) => (c.TableAlias = resolvedAlias));
|
|
948
|
+
this._statements.forEach((c) => (c.TableAlias = resolvedAlias));
|
|
960
949
|
return this;
|
|
961
950
|
}
|
|
962
951
|
clone() {
|
|
@@ -967,11 +956,18 @@ let SelectQueryBuilder = SelectQueryBuilder_1 = class SelectQueryBuilder extends
|
|
|
967
956
|
builder._joinStatements = this._joinStatements.map(c => c.clone(builder));
|
|
968
957
|
// Clone statements with mapped WhereBuilder references
|
|
969
958
|
builder._statements = this._statements.map(c => c.clone(builder));
|
|
959
|
+
// Clone group-by statements (previously dropped, silently losing GROUP BY
|
|
960
|
+
// on a cloned query — eg. orm-api count-pagination clones the builder).
|
|
961
|
+
builder._groupStatements = this._groupStatements.map(c => c.clone(builder));
|
|
962
|
+
// Carry relations and result middlewares over. These hold live objects and
|
|
963
|
+
// are shared the same way mergeRelations()/mergeBuilder() already share them.
|
|
964
|
+
builder._relations = [...this._relations];
|
|
965
|
+
builder._middlewares = [...this._middlewares];
|
|
970
966
|
/**
|
|
971
967
|
* ------------------------------------------------------------------
|
|
972
968
|
*/
|
|
973
969
|
builder._limit = { ...this._limit };
|
|
974
|
-
builder.
|
|
970
|
+
builder._sorts = this._sorts.map((s) => ({ ...s }));
|
|
975
971
|
builder._boolean = this._boolean;
|
|
976
972
|
builder._distinct = this._distinct;
|
|
977
973
|
builder._table = this._table;
|
|
@@ -1070,15 +1066,21 @@ let SelectQueryBuilder = SelectQueryBuilder_1 = class SelectQueryBuilder extends
|
|
|
1070
1066
|
this._getRelationInstance(relation).execute(callback);
|
|
1071
1067
|
return this;
|
|
1072
1068
|
}
|
|
1073
|
-
mergeBuilder(builder) {
|
|
1069
|
+
mergeBuilder(builder, includeStatements = true) {
|
|
1074
1070
|
this._columns = this._columns.concat(builder._columns);
|
|
1075
1071
|
this._cteStatement = builder._cteStatement;
|
|
1076
1072
|
this._distinct = builder._distinct;
|
|
1077
|
-
this
|
|
1078
|
-
|
|
1079
|
-
|
|
1080
|
-
|
|
1081
|
-
|
|
1073
|
+
// Fold the merged builder's sorts into this query's sorts (multi-column
|
|
1074
|
+
// ORDER BY). If the merged builder has no sorts, this keeps our own.
|
|
1075
|
+
this._sorts = this._sorts.concat(builder._sorts.map((s) => ({ ...s })));
|
|
1076
|
+
// `includeStatements: false` is used by JoinStatement so that a join
|
|
1077
|
+
// callback's WHERE conditions are NOT folded into the main query's WHERE
|
|
1078
|
+
// (which silently turns a LEFT JOIN into an inner filter). The join emits
|
|
1079
|
+
// those conditions in its own ON clause instead. Columns/sort are still
|
|
1080
|
+
// merged so join-callback selects (e.g. extra joined columns) keep working.
|
|
1081
|
+
if (includeStatements) {
|
|
1082
|
+
this.mergeStatements(builder);
|
|
1083
|
+
}
|
|
1082
1084
|
}
|
|
1083
1085
|
mergeRelations(builder) {
|
|
1084
1086
|
this._relations = this._relations.concat(builder._relations);
|
|
@@ -1089,6 +1091,21 @@ let SelectQueryBuilder = SelectQueryBuilder_1 = class SelectQueryBuilder extends
|
|
|
1089
1091
|
this._joinStatements = this._joinStatements.concat(builder._joinStatements);
|
|
1090
1092
|
this._statements = this._statements.concat(stms);
|
|
1091
1093
|
}
|
|
1094
|
+
/**
|
|
1095
|
+
* Includes soft-deleted rows in the result set by removing the default
|
|
1096
|
+
* `DeletedAt IS NULL` filter added by createQuery for @SoftDelete models.
|
|
1097
|
+
*/
|
|
1098
|
+
withDeleted() {
|
|
1099
|
+
const descriptor = (0, descriptor_js_1.extractModelDescriptor)(this._model);
|
|
1100
|
+
const deletedAt = descriptor?.SoftDelete?.DeletedAt;
|
|
1101
|
+
if (!deletedAt) {
|
|
1102
|
+
return this;
|
|
1103
|
+
}
|
|
1104
|
+
this._statements = this._statements.filter((s) => {
|
|
1105
|
+
return !(s instanceof statements_js_1.WhereStatement && s.Column === deletedAt && s.Operator === enums_js_1.SqlOperator.NULL);
|
|
1106
|
+
});
|
|
1107
|
+
return this;
|
|
1108
|
+
}
|
|
1092
1109
|
min(column, as) {
|
|
1093
1110
|
this._columns.push(this._container.resolve(statements_js_1.ColumnMethodStatement, [column, enums_js_1.ColumnMethods.MIN, as, this._tableAlias]));
|
|
1094
1111
|
return this;
|
|
@@ -1130,44 +1147,26 @@ let SelectQueryBuilder = SelectQueryBuilder_1 = class SelectQueryBuilder extends
|
|
|
1130
1147
|
return compiler.compile();
|
|
1131
1148
|
}
|
|
1132
1149
|
async all() {
|
|
1133
|
-
return await this;
|
|
1150
|
+
return await this.execute();
|
|
1134
1151
|
}
|
|
1135
1152
|
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);
|
|
1153
|
+
const res = await this.execute();
|
|
1154
|
+
if (Array.isArray(res)) {
|
|
1155
|
+
return res.length > 0;
|
|
1156
|
+
}
|
|
1157
|
+
return res !== undefined && res !== null;
|
|
1168
1158
|
}
|
|
1169
|
-
|
|
1170
|
-
|
|
1159
|
+
/**
|
|
1160
|
+
* Overrides the engine rather than `execute()` so that the `takeFirst()` unwrapping
|
|
1161
|
+
* happens *inside* the memo. `beforeQueryExecution` is dispatched by `Builder._run()`
|
|
1162
|
+
* for every builder type, so it still fires once per builder rather than once per await.
|
|
1163
|
+
*/
|
|
1164
|
+
async _run() {
|
|
1165
|
+
const result = await super._run();
|
|
1166
|
+
if (this._first && Array.isArray(result)) {
|
|
1167
|
+
return (result.length !== 0 ? result[0] : undefined);
|
|
1168
|
+
}
|
|
1169
|
+
return result;
|
|
1171
1170
|
}
|
|
1172
1171
|
};
|
|
1173
1172
|
exports.SelectQueryBuilder = SelectQueryBuilder;
|
|
@@ -1193,6 +1192,10 @@ let DeleteQueryBuilder = class DeleteQueryBuilder extends QueryBuilder {
|
|
|
1193
1192
|
offset: -1,
|
|
1194
1193
|
};
|
|
1195
1194
|
this.QueryContext = interfaces_js_1.QueryContext.Delete;
|
|
1195
|
+
// Query middlewares (e.g. rbac ownership enforcement) must run for deletes
|
|
1196
|
+
// too, not only selects — otherwise :own permission constraints are never
|
|
1197
|
+
// applied and any row can be deleted.
|
|
1198
|
+
this._queryMiddlewares.forEach((x) => x.afterQueryCreation(this));
|
|
1196
1199
|
}
|
|
1197
1200
|
toDB() {
|
|
1198
1201
|
return this._container.resolve(interfaces_js_1.DeleteQueryCompiler, [this]).compile();
|
|
@@ -1232,6 +1235,9 @@ let OnDuplicateQueryBuilder = class OnDuplicateQueryBuilder {
|
|
|
1232
1235
|
this._columnsToUpdate = columns;
|
|
1233
1236
|
return this;
|
|
1234
1237
|
}
|
|
1238
|
+
execute() {
|
|
1239
|
+
return this._parent.execute();
|
|
1240
|
+
}
|
|
1235
1241
|
then(onfulfilled, onrejected) {
|
|
1236
1242
|
return this._parent.then(onfulfilled, onrejected);
|
|
1237
1243
|
}
|
|
@@ -1255,6 +1261,10 @@ let UpdateQueryBuilder = class UpdateQueryBuilder extends QueryBuilder {
|
|
|
1255
1261
|
this._boolean = enums_js_1.WhereBoolean.AND;
|
|
1256
1262
|
this._statements = [];
|
|
1257
1263
|
this.QueryContext = interfaces_js_1.QueryContext.Update;
|
|
1264
|
+
// Query middlewares (e.g. rbac ownership enforcement) must run for updates
|
|
1265
|
+
// too, not only selects — otherwise :own permission constraints are never
|
|
1266
|
+
// applied and any row can be updated.
|
|
1267
|
+
this._queryMiddlewares.forEach((x) => x.afterQueryCreation(this));
|
|
1258
1268
|
}
|
|
1259
1269
|
in(name) {
|
|
1260
1270
|
this.setTable(name);
|
|
@@ -1290,6 +1300,10 @@ let InsertQueryBuilder = class InsertQueryBuilder extends QueryBuilder {
|
|
|
1290
1300
|
get Replace() {
|
|
1291
1301
|
return this._replace;
|
|
1292
1302
|
}
|
|
1303
|
+
/** Columns requested via {@link returning}. Empty when no RETURNING clause was asked for. */
|
|
1304
|
+
get Returning() {
|
|
1305
|
+
return this._returning ?? [];
|
|
1306
|
+
}
|
|
1293
1307
|
constructor(container, driver, model) {
|
|
1294
1308
|
super(container, driver, model);
|
|
1295
1309
|
this._update = false;
|
|
@@ -1298,6 +1312,52 @@ let InsertQueryBuilder = class InsertQueryBuilder extends QueryBuilder {
|
|
|
1298
1312
|
this._columns = [];
|
|
1299
1313
|
this._values = [];
|
|
1300
1314
|
this.QueryContext = interfaces_js_1.QueryContext.Insert;
|
|
1315
|
+
// Inserts get the same middleware pass as the other three builders. Note that a
|
|
1316
|
+
// middleware which needs to see or amend the row payload must use
|
|
1317
|
+
// `beforeQueryExecution` instead — at construction time `values()` has not been called.
|
|
1318
|
+
this._queryMiddlewares.forEach((x) => x.afterQueryCreation(this));
|
|
1319
|
+
}
|
|
1320
|
+
/**
|
|
1321
|
+
* The value of `column` on every row about to be inserted, in row order. Empty when the
|
|
1322
|
+
* payload does not carry that column at all.
|
|
1323
|
+
*
|
|
1324
|
+
* The read counterpart of {@link forceColumn}, and the only sane way for a
|
|
1325
|
+
* `beforeQueryExecution` hook to inspect what is being written: the payload lives in two
|
|
1326
|
+
* parallel structures ( `_columns` and one array per row ), so every caller would
|
|
1327
|
+
* otherwise repeat the same index lookup.
|
|
1328
|
+
*
|
|
1329
|
+
* @param column - column to read off each row
|
|
1330
|
+
*/
|
|
1331
|
+
getColumnValues(column) {
|
|
1332
|
+
const index = this._columns.findIndex((c) => !(c.Column instanceof RawQuery) && c.Column === column);
|
|
1333
|
+
if (index === -1) {
|
|
1334
|
+
return [];
|
|
1335
|
+
}
|
|
1336
|
+
// eslint-disable-next-line security/detect-object-injection
|
|
1337
|
+
return this._values.map((row) => row[index]);
|
|
1338
|
+
}
|
|
1339
|
+
/**
|
|
1340
|
+
* Forces `column` to `value` on every row of the payload, overwriting whatever the caller
|
|
1341
|
+
* supplied.
|
|
1342
|
+
*
|
|
1343
|
+
* This is the write path for a policy that must not be negotiable — rbac's `createOwn`
|
|
1344
|
+
* ownership stamp. `values()` cannot serve: it appends rows and takes the column list from
|
|
1345
|
+
* its own argument, so calling it a second time adds a row rather than amending the
|
|
1346
|
+
* existing ones.
|
|
1347
|
+
*
|
|
1348
|
+
* @param column - column to overwrite on every row
|
|
1349
|
+
* @param value - value to force
|
|
1350
|
+
*/
|
|
1351
|
+
forceColumn(column, value) {
|
|
1352
|
+
const existing = this._columns.findIndex((c) => !(c.Column instanceof RawQuery) && c.Column === column);
|
|
1353
|
+
if (existing === -1) {
|
|
1354
|
+
this.columns([...this._columns.filter((c) => !(c.Column instanceof RawQuery)).map((c) => c.Column), column]);
|
|
1355
|
+
this._values.forEach((row) => row.push(value));
|
|
1356
|
+
return this;
|
|
1357
|
+
}
|
|
1358
|
+
// eslint-disable-next-line security/detect-object-injection
|
|
1359
|
+
this._values.forEach((row) => (row[existing] = value));
|
|
1360
|
+
return this;
|
|
1301
1361
|
}
|
|
1302
1362
|
/**
|
|
1303
1363
|
* Sets insert to ignore on duplicate
|
|
@@ -1310,8 +1370,21 @@ let InsertQueryBuilder = class InsertQueryBuilder extends QueryBuilder {
|
|
|
1310
1370
|
this._replace = true;
|
|
1311
1371
|
return this;
|
|
1312
1372
|
}
|
|
1373
|
+
/**
|
|
1374
|
+
* Asks the dialect to echo the given columns of every inserted row back.
|
|
1375
|
+
*
|
|
1376
|
+
* @throws NotSupported on drivers whose `supportedFeatures().insertReturning` is false —
|
|
1377
|
+
* silently doing nothing is how this API was a no-op on MySQL and MSSQL for years.
|
|
1378
|
+
*/
|
|
1313
1379
|
returning(columns) {
|
|
1380
|
+
if (!this.Driver.supportedFeatures().insertReturning) {
|
|
1381
|
+
throw new exceptions_1.NotSupported(`driver ${this.Driver.Options.Driver} does not support RETURNING on INSERT`);
|
|
1382
|
+
}
|
|
1314
1383
|
this._returning = columns;
|
|
1384
|
+
// onDuplicate() sets Upsert unconditionally and wins if it runs afterwards.
|
|
1385
|
+
if (this.QueryContext === interfaces_js_1.QueryContext.Insert) {
|
|
1386
|
+
this.QueryContext = interfaces_js_1.QueryContext.InsertReturning;
|
|
1387
|
+
}
|
|
1315
1388
|
return this;
|
|
1316
1389
|
}
|
|
1317
1390
|
values(data) {
|
|
@@ -1473,6 +1546,7 @@ let ColumnQueryBuilder = class ColumnQueryBuilder {
|
|
|
1473
1546
|
this.Comment = '';
|
|
1474
1547
|
this.Unique = false;
|
|
1475
1548
|
this.Unsigned = false;
|
|
1549
|
+
this.InlinePrimaryKey = true;
|
|
1476
1550
|
this.Args.push(...args);
|
|
1477
1551
|
}
|
|
1478
1552
|
notNull() {
|
|
@@ -1643,10 +1717,11 @@ exports.AlterTableQueryBuilder = AlterTableQueryBuilder = __decorate([
|
|
|
1643
1717
|
], AlterTableQueryBuilder);
|
|
1644
1718
|
let TableQueryBuilder = class TableQueryBuilder extends QueryBuilder {
|
|
1645
1719
|
/**
|
|
1646
|
-
* Alias for
|
|
1720
|
+
* Alias for binary(name, 16 ) - uuids are stored as 16-byte BINARY to match
|
|
1721
|
+
* the UuidConverter ( which writes a dashed uuid as a 16-byte buffer ).
|
|
1647
1722
|
*/
|
|
1648
1723
|
uuid(name) {
|
|
1649
|
-
return this.
|
|
1724
|
+
return this.binary(name, 16);
|
|
1650
1725
|
}
|
|
1651
1726
|
ifExists() {
|
|
1652
1727
|
this._checkExists = true;
|
|
@@ -2053,6 +2128,16 @@ function createQuery(model, query, injectModel = true) {
|
|
|
2053
2128
|
}
|
|
2054
2129
|
qr.middleware(new discrimination_middleware_js_1.DiscriminationMapMiddleware(dsc));
|
|
2055
2130
|
qr.setTable(dsc.TableName);
|
|
2131
|
+
// Soft-delete read filtering: by default exclude rows that have been soft
|
|
2132
|
+
// deleted (DeletedAt IS NOT NULL). SelectQueryBuilder.withDeleted() removes
|
|
2133
|
+
// this default statement to include soft-deleted rows again.
|
|
2134
|
+
// Guarded on the DeletedAt column actually being present in the model's
|
|
2135
|
+
// reflected columns — a filter on a column the schema does not expose is
|
|
2136
|
+
// impossible anyway, and the guard keeps queries working when table info
|
|
2137
|
+
// has not (yet) surfaced the column.
|
|
2138
|
+
if (qr instanceof SelectQueryBuilder && dsc.SoftDelete?.DeletedAt && dsc.Columns?.some((c) => c.Name === dsc.SoftDelete.DeletedAt)) {
|
|
2139
|
+
qr.whereNull(dsc.SoftDelete.DeletedAt);
|
|
2140
|
+
}
|
|
2056
2141
|
if (driver.Options.Database) {
|
|
2057
2142
|
qr.database(driver.Options.Database);
|
|
2058
2143
|
}
|