vsrepo 2.6.0 → 2.7.0
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/CHANGELOG.md +24 -0
- package/README.md +3 -2
- package/README.pt-BR.md +3 -2
- package/dist/VSRepoAdapter.d.ts +12 -0
- package/dist/VSRepoAdapter.js.map +1 -1
- package/dist/VSRepository.d.ts +50 -4
- package/dist/VSRepository.js +57 -28
- package/dist/VSRepository.js.map +1 -1
- package/dist/decorators/query-method.decorator.d.ts +3 -24
- package/dist/decorators/query-method.decorator.js +4 -27
- package/dist/decorators/query-method.decorator.js.map +1 -1
- package/dist/index.d.ts +4 -0
- package/dist/index.js +5 -1
- package/dist/index.js.map +1 -1
- package/dist/internal/resolvers/dynamic-methods.resolver.d.ts +6 -5
- package/dist/internal/resolvers/dynamic-methods.resolver.js +112 -98
- package/dist/internal/resolvers/dynamic-methods.resolver.js.map +1 -1
- package/dist/internal/utils/vs-placeholder-parser.util.d.ts +2 -0
- package/dist/internal/utils/vs-placeholder-parser.util.js +62 -0
- package/dist/internal/utils/vs-placeholder-parser.util.js.map +1 -0
- package/dist/internal/utils/vs-query-builder.util.d.ts +5 -1
- package/dist/internal/utils/vs-query-builder.util.js +1 -13
- package/dist/internal/utils/vs-query-builder.util.js.map +1 -1
- package/dist/internal/utils/vs-raw-query-builder.util.d.ts +238 -0
- package/dist/internal/utils/vs-raw-query-builder.util.js +433 -0
- package/dist/internal/utils/vs-raw-query-builder.util.js.map +1 -0
- package/dist/internal/utils/vs-sql.util.d.ts +100 -0
- package/dist/internal/utils/vs-sql.util.js +151 -0
- package/dist/internal/utils/vs-sql.util.js.map +1 -0
- package/dist/internal/validators/decorators.validator.js +1 -1
- package/dist/internal/validators/decorators.validator.js.map +1 -1
- package/dist/internal/validators/vsrepo.validator.js +1 -0
- package/dist/internal/validators/vsrepo.validator.js.map +1 -1
- package/dist/types/vsrepo/vs-raw-query-builder-cte-query.type.d.ts +8 -0
- package/dist/types/vsrepo/vs-raw-query-builder-cte-query.type.js +3 -0
- package/dist/types/vsrepo/vs-raw-query-builder-cte-query.type.js.map +1 -0
- package/dist/types/vsrepo/vs-raw-query-builder-target.type.d.ts +7 -0
- package/dist/types/vsrepo/vs-raw-query-builder-target.type.js +3 -0
- package/dist/types/vsrepo/vs-raw-query-builder-target.type.js.map +1 -0
- package/dist/types/vsrepo/vsrepo-options.type.d.ts +18 -0
- package/package.json +3 -2
|
@@ -0,0 +1,433 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
+
exports.VSRawQueryBuilder = void 0;
|
|
4
|
+
const VSRepoError_1 = require("../../errors/VSRepoError");
|
|
5
|
+
const vsrepo_error_type_enum_1 = require("../enums/vsrepo-error-type.enum");
|
|
6
|
+
const vs_sql_util_1 = require("./vs-sql.util");
|
|
7
|
+
/**
|
|
8
|
+
* Fluent, SQL-agnostic builder for hand-written **`SELECT`** queries whose shape is only known
|
|
9
|
+
* at runtime, but whose SQL is too specific (window functions, CTEs referenced elsewhere,
|
|
10
|
+
* vendor-specific syntax, ...) to express through {@link VSQueryBuilder}'s `where`/`relations`
|
|
11
|
+
* model. Compiles down to a single {@link VSSql} fragment — get one from
|
|
12
|
+
* `VSRepository.createRawQueryBuilder()`.
|
|
13
|
+
*
|
|
14
|
+
* Nothing reaches the database until {@link VSRawQueryBuilder.execute} is called. The builder is
|
|
15
|
+
* **mutable**: every chained call changes the same instance and returns it. Use
|
|
16
|
+
* {@link VSRawQueryBuilder.clone} to derive variations from a common base.
|
|
17
|
+
*
|
|
18
|
+
* @example
|
|
19
|
+
* ```typescript
|
|
20
|
+
* import { VSSql } from "vsrepo";
|
|
21
|
+
*
|
|
22
|
+
* const rows = await orderRepository
|
|
23
|
+
* .createRawQueryBuilder()
|
|
24
|
+
* .select("o.id", "o.total", "u.name")
|
|
25
|
+
* .from("order", "o")
|
|
26
|
+
* .innerJoin("user", "u", "u.id = o.user_id")
|
|
27
|
+
* .where(VSSql.sql`u.active = ${true}`)
|
|
28
|
+
* .andWhere(VSSql.sql`o.total > ${100}`)
|
|
29
|
+
* .andWhere("o.deleted_at is null")
|
|
30
|
+
* .groupBy("o.id", "u.name")
|
|
31
|
+
* .having(VSSql.sql`count(*) > ${1}`)
|
|
32
|
+
* .orderBy("o.total", "desc")
|
|
33
|
+
* .limit(20)
|
|
34
|
+
* .offset(0)
|
|
35
|
+
* .execute<{ id: string; total: number; name: string }[]>();
|
|
36
|
+
* ```
|
|
37
|
+
*
|
|
38
|
+
* @publicApi
|
|
39
|
+
*/
|
|
40
|
+
class VSRawQueryBuilder {
|
|
41
|
+
db;
|
|
42
|
+
adapter;
|
|
43
|
+
logger;
|
|
44
|
+
cteClauses = [];
|
|
45
|
+
recursiveWith = false;
|
|
46
|
+
selectColumns = [];
|
|
47
|
+
fromTarget;
|
|
48
|
+
joinClauses = [];
|
|
49
|
+
whereConditions = [];
|
|
50
|
+
groupByColumns = [];
|
|
51
|
+
havingConditions = [];
|
|
52
|
+
orderByClauses = [];
|
|
53
|
+
limitValue;
|
|
54
|
+
offsetValue;
|
|
55
|
+
/**
|
|
56
|
+
* @internal
|
|
57
|
+
*/
|
|
58
|
+
constructor(db, adapter, logger) {
|
|
59
|
+
this.db = db;
|
|
60
|
+
this.adapter = adapter;
|
|
61
|
+
this.logger = logger;
|
|
62
|
+
}
|
|
63
|
+
trace(message, obj) {
|
|
64
|
+
this.logger?.logDebug(`VSRawQueryBuilder: ${message}`, obj);
|
|
65
|
+
}
|
|
66
|
+
static validateNonEmptyString(value, context) {
|
|
67
|
+
if (value.trim().length === 0) {
|
|
68
|
+
throw new VSRepoError_1.VSRepoError(`${context}: Invalid value: Expected a non-empty string but received ${value}`, vsrepo_error_type_enum_1.VSRepoErrorType.QUERY_BUILDER);
|
|
69
|
+
}
|
|
70
|
+
}
|
|
71
|
+
/** Converts a raw string into a `VSSql.raw` fragment (validating it isn't empty/blank first,
|
|
72
|
+
* since that would silently compile into broken SQL — e.g. a trailing comma or an empty
|
|
73
|
+
* `WHERE ()`) or passes an already-built `VSSql` fragment through untouched.
|
|
74
|
+
*
|
|
75
|
+
* @param context Name of the calling method/argument, used in the `VSRepoError` message. */
|
|
76
|
+
static toFragment(value, context) {
|
|
77
|
+
if (typeof value !== "string") {
|
|
78
|
+
return value;
|
|
79
|
+
}
|
|
80
|
+
VSRawQueryBuilder.validateNonEmptyString(value, context);
|
|
81
|
+
return vs_sql_util_1.VSSql.raw(value);
|
|
82
|
+
}
|
|
83
|
+
newSubBuilder() {
|
|
84
|
+
return new VSRawQueryBuilder(this.db, this.adapter, this.logger);
|
|
85
|
+
}
|
|
86
|
+
resolveSubquery(value) {
|
|
87
|
+
const result = typeof value === "function" ? value(this.newSubBuilder()) : value;
|
|
88
|
+
return result instanceof VSRawQueryBuilder ? result.toVSSql() : result;
|
|
89
|
+
}
|
|
90
|
+
resolveTarget(target, context, alias) {
|
|
91
|
+
const base = target instanceof VSRawQueryBuilder || typeof target === "function"
|
|
92
|
+
? vs_sql_util_1.VSSql.sql `(${this.resolveSubquery(target)})`
|
|
93
|
+
: VSRawQueryBuilder.toFragment(target, context);
|
|
94
|
+
if (alias === undefined) {
|
|
95
|
+
return base;
|
|
96
|
+
}
|
|
97
|
+
VSRawQueryBuilder.validateNonEmptyString(alias, "alias");
|
|
98
|
+
return vs_sql_util_1.VSSql.sql `${base} AS ${vs_sql_util_1.VSSql.raw(alias)}`;
|
|
99
|
+
}
|
|
100
|
+
resolveCteQuery(query) {
|
|
101
|
+
return query instanceof vs_sql_util_1.VSSql ? query : this.resolveSubquery(query);
|
|
102
|
+
}
|
|
103
|
+
static combine(conditions) {
|
|
104
|
+
let result = vs_sql_util_1.VSSql.sql `(${conditions[0].sql})`;
|
|
105
|
+
for (let i = 1; i < conditions.length; i++) {
|
|
106
|
+
const { connector, sql } = conditions[i];
|
|
107
|
+
result = connector === "AND" ? vs_sql_util_1.VSSql.sql `${result} AND (${sql})` : vs_sql_util_1.VSSql.sql `${result} OR (${sql})`;
|
|
108
|
+
}
|
|
109
|
+
return result;
|
|
110
|
+
}
|
|
111
|
+
static validateNonNegativeInt(value, name) {
|
|
112
|
+
if (!Number.isInteger(value) || value < 0) {
|
|
113
|
+
throw new VSRepoError_1.VSRepoError(`${name}: Invalid value: Expected >=0 but received ${value}`, vsrepo_error_type_enum_1.VSRepoErrorType.QUERY_BUILDER);
|
|
114
|
+
}
|
|
115
|
+
}
|
|
116
|
+
/**
|
|
117
|
+
* Sets the columns to select, replacing any previous `select`. Each column is either a raw,
|
|
118
|
+
* trusted string (an identifier, passed through as-is — like `VSSql.raw`, never pass
|
|
119
|
+
* user-controlled input) or a `VSSql` fragment for anything parameterized or aliased
|
|
120
|
+
* (**VSSql.sql\`count(*) AS total\`**).
|
|
121
|
+
*
|
|
122
|
+
* @param columns One or more columns/expressions. `select()` with no arguments is equivalent
|
|
123
|
+
* to `SELECT *`.
|
|
124
|
+
*/
|
|
125
|
+
select(...columns) {
|
|
126
|
+
this.selectColumns = columns.length
|
|
127
|
+
? columns.map(column => VSRawQueryBuilder.toFragment(column, "select"))
|
|
128
|
+
: [vs_sql_util_1.VSSql.raw("*")];
|
|
129
|
+
return this;
|
|
130
|
+
}
|
|
131
|
+
/**
|
|
132
|
+
* Sets the `FROM` target, replacing any previous one.
|
|
133
|
+
*
|
|
134
|
+
* @param target A raw table name, a `VSSql` fragment, or another `VSRawQueryBuilder`
|
|
135
|
+
* (compiled inline as a subquery, wrapped in parentheses).
|
|
136
|
+
* @param alias Optional alias, appended as `AS alias` (raw, trusted text).
|
|
137
|
+
*/
|
|
138
|
+
from(target, alias) {
|
|
139
|
+
this.fromTarget = this.resolveTarget(target, "from", alias);
|
|
140
|
+
return this;
|
|
141
|
+
}
|
|
142
|
+
addCte(name, query, columns) {
|
|
143
|
+
VSRawQueryBuilder.validateNonEmptyString(name, "with");
|
|
144
|
+
if (columns) {
|
|
145
|
+
for (let i = 0; i < columns.length; i++) {
|
|
146
|
+
const column = columns[i];
|
|
147
|
+
VSRawQueryBuilder.validateNonEmptyString(column, `with columns[${i}]`);
|
|
148
|
+
}
|
|
149
|
+
}
|
|
150
|
+
this.cteClauses.push({ name, columns, query: this.resolveCteQuery(query) });
|
|
151
|
+
return this;
|
|
152
|
+
}
|
|
153
|
+
/**
|
|
154
|
+
* Adds a `WITH` (common table expression). Each call adds one CTE; call it
|
|
155
|
+
* again to add more — they're all listed under a single `WITH`, in the order added.
|
|
156
|
+
*
|
|
157
|
+
* @param name Name the CTE is referenced by elsewhere in the query (raw, trusted text).
|
|
158
|
+
* @param query The CTE's body: a `VSRawQueryBuilder`, a subquery function, or a `VSSql` fragment — needed for
|
|
159
|
+
* anything a single `SELECT` builder can't express (e.g. a `UNION`).
|
|
160
|
+
* @param columns Optional explicit column list, rendered as `name(col1, col2) AS (...)`.
|
|
161
|
+
*
|
|
162
|
+
* @example
|
|
163
|
+
* ```typescript
|
|
164
|
+
* const rows = await orderRepository
|
|
165
|
+
* .createRawQueryBuilder()
|
|
166
|
+
* .with("big_spenders", qb => qb.select("user_id").from("order").groupBy("user_id").having("sum(total) > 1000"))
|
|
167
|
+
* .select("u.*")
|
|
168
|
+
* .from("user", "u")
|
|
169
|
+
* .innerJoin("big_spenders", "bs", "bs.user_id = u.id")
|
|
170
|
+
* .execute();
|
|
171
|
+
* ```
|
|
172
|
+
*/
|
|
173
|
+
with(name, query, columns) {
|
|
174
|
+
return this.addCte(name, query, columns);
|
|
175
|
+
}
|
|
176
|
+
/**
|
|
177
|
+
* Same as {@link VSRawQueryBuilder.with}, but marks the whole `WITH` clause as `RECURSIVE`
|
|
178
|
+
* (required by the SQL standard for a CTE that references itself in its own body — usually
|
|
179
|
+
* a `VSSql` fragment with a `... UNION ALL SELECT ... FROM name ...` shape). One recursive
|
|
180
|
+
* CTE is enough to make the whole clause `WITH RECURSIVE`, even when combined with other,
|
|
181
|
+
* non-recursive ones added via {@link VSRawQueryBuilder.with}.
|
|
182
|
+
*
|
|
183
|
+
* @example
|
|
184
|
+
* ```typescript
|
|
185
|
+
* const orgChart = await employeeRepository
|
|
186
|
+
* .createRawQueryBuilder()
|
|
187
|
+
* .withRecursive(
|
|
188
|
+
* "subordinates",
|
|
189
|
+
* VSSql.sql`
|
|
190
|
+
* SELECT id, manager_id, 1 AS depth FROM employee WHERE id = ${managerId}
|
|
191
|
+
* UNION ALL
|
|
192
|
+
* SELECT e.id, e.manager_id, s.depth + 1 FROM employee e
|
|
193
|
+
* INNER JOIN subordinates s ON e.manager_id = s.id
|
|
194
|
+
* `,
|
|
195
|
+
* ["id", "manager_id", "depth"],
|
|
196
|
+
* )
|
|
197
|
+
* .select("*")
|
|
198
|
+
* .from("subordinates")
|
|
199
|
+
* .orderBy("depth")
|
|
200
|
+
* .execute();
|
|
201
|
+
* ```
|
|
202
|
+
*/
|
|
203
|
+
withRecursive(name, query, columns) {
|
|
204
|
+
this.recursiveWith = true;
|
|
205
|
+
return this.addCte(name, query, columns);
|
|
206
|
+
}
|
|
207
|
+
addJoin(type, target, alias, on) {
|
|
208
|
+
this.joinClauses.push({
|
|
209
|
+
type,
|
|
210
|
+
target: this.resolveTarget(target, "join", alias),
|
|
211
|
+
on: VSRawQueryBuilder.toFragment(on, "on"),
|
|
212
|
+
});
|
|
213
|
+
return this;
|
|
214
|
+
}
|
|
215
|
+
/**
|
|
216
|
+
* Adds an `INNER JOIN`.
|
|
217
|
+
*/
|
|
218
|
+
innerJoin(target, alias, on) {
|
|
219
|
+
return this.addJoin("INNER", target, alias, on);
|
|
220
|
+
}
|
|
221
|
+
/**
|
|
222
|
+
* Adds a `LEFT JOIN`.
|
|
223
|
+
*/
|
|
224
|
+
leftJoin(target, alias, on) {
|
|
225
|
+
return this.addJoin("LEFT", target, alias, on);
|
|
226
|
+
}
|
|
227
|
+
/**
|
|
228
|
+
* Adds a `RIGHT JOIN`.
|
|
229
|
+
*/
|
|
230
|
+
rightJoin(target, alias, on) {
|
|
231
|
+
return this.addJoin("RIGHT", target, alias, on);
|
|
232
|
+
}
|
|
233
|
+
/**
|
|
234
|
+
* Adds a `FULL JOIN`.
|
|
235
|
+
*/
|
|
236
|
+
fullJoin(target, alias, on) {
|
|
237
|
+
return this.addJoin("FULL", target, alias, on);
|
|
238
|
+
}
|
|
239
|
+
/**
|
|
240
|
+
* Adds a `WHERE` condition. The first call sets the filter; every later call (`where` or
|
|
241
|
+
* {@link VSRawQueryBuilder.andWhere}) is `AND`-combined with it, each wrapped in parentheses.
|
|
242
|
+
* Use {@link VSRawQueryBuilder.orWhere} to `OR`-combine instead.
|
|
243
|
+
*/
|
|
244
|
+
where(condition) {
|
|
245
|
+
this.whereConditions.push({ connector: "AND", sql: VSRawQueryBuilder.toFragment(condition, "where") });
|
|
246
|
+
return this;
|
|
247
|
+
}
|
|
248
|
+
/** Alias for {@link VSRawQueryBuilder.where} — `AND`-combines `condition` with the existing filter. */
|
|
249
|
+
andWhere(condition) {
|
|
250
|
+
this.whereConditions.push({ connector: "AND", sql: VSRawQueryBuilder.toFragment(condition, "andWhere") });
|
|
251
|
+
return this;
|
|
252
|
+
}
|
|
253
|
+
/** `OR`-combines `condition` with the existing `WHERE` filter. */
|
|
254
|
+
orWhere(condition) {
|
|
255
|
+
this.whereConditions.push({ connector: "OR", sql: VSRawQueryBuilder.toFragment(condition, "orWhere") });
|
|
256
|
+
return this;
|
|
257
|
+
}
|
|
258
|
+
/**
|
|
259
|
+
* Adds columns to `GROUP BY`. Each call appends; call {@link VSRawQueryBuilder.clone} from a
|
|
260
|
+
* common base if you need independent variations.
|
|
261
|
+
*/
|
|
262
|
+
groupBy(...columns) {
|
|
263
|
+
this.groupByColumns.push(...columns.map(column => VSRawQueryBuilder.toFragment(column, "groupBy")));
|
|
264
|
+
return this;
|
|
265
|
+
}
|
|
266
|
+
/**
|
|
267
|
+
* Adds a `HAVING` condition, `AND`-combined with any previous one (same semantics as
|
|
268
|
+
* {@link VSRawQueryBuilder.where}). Use {@link VSRawQueryBuilder.orHaving} to `OR`-combine.
|
|
269
|
+
*/
|
|
270
|
+
having(condition) {
|
|
271
|
+
this.havingConditions.push({ connector: "AND", sql: VSRawQueryBuilder.toFragment(condition, "having") });
|
|
272
|
+
return this;
|
|
273
|
+
}
|
|
274
|
+
/** Alias for {@link VSRawQueryBuilder.having} — `AND`-combines `condition` with the existing `HAVING` filter. */
|
|
275
|
+
andHaving(condition) {
|
|
276
|
+
this.havingConditions.push({ connector: "AND", sql: VSRawQueryBuilder.toFragment(condition, "andHaving") });
|
|
277
|
+
return this;
|
|
278
|
+
}
|
|
279
|
+
/** `OR`-combines `condition` with the existing `HAVING` filter. */
|
|
280
|
+
orHaving(condition) {
|
|
281
|
+
this.havingConditions.push({ connector: "OR", sql: VSRawQueryBuilder.toFragment(condition, "orHaving") });
|
|
282
|
+
return this;
|
|
283
|
+
}
|
|
284
|
+
/**
|
|
285
|
+
* Adds a column to `ORDER BY`. Each call appends, in order, so call it once per column for a
|
|
286
|
+
* multi-column ordering.
|
|
287
|
+
*/
|
|
288
|
+
orderBy(column, direction) {
|
|
289
|
+
const base = VSRawQueryBuilder.toFragment(column, "orderBy");
|
|
290
|
+
this.orderByClauses.push(direction ? vs_sql_util_1.VSSql.sql `${base} ${vs_sql_util_1.VSSql.raw(direction.toUpperCase())}` : base);
|
|
291
|
+
return this;
|
|
292
|
+
}
|
|
293
|
+
/**
|
|
294
|
+
* Sets the maximum number of rows to return, replacing any previous `limit`.
|
|
295
|
+
*/
|
|
296
|
+
limit(limit) {
|
|
297
|
+
VSRawQueryBuilder.validateNonNegativeInt(limit, "limit");
|
|
298
|
+
this.limitValue = limit;
|
|
299
|
+
return this;
|
|
300
|
+
}
|
|
301
|
+
/**
|
|
302
|
+
* Sets how many rows to skip, replacing any previous `offset`.
|
|
303
|
+
*/
|
|
304
|
+
offset(offset) {
|
|
305
|
+
VSRawQueryBuilder.validateNonNegativeInt(offset, "offset");
|
|
306
|
+
this.offsetValue = offset;
|
|
307
|
+
return this;
|
|
308
|
+
}
|
|
309
|
+
/**
|
|
310
|
+
* Sets the client or transaction the query runs on, replacing the one given to
|
|
311
|
+
* `createRawQueryBuilder()`.
|
|
312
|
+
*
|
|
313
|
+
* It's lazy — it only matters when {@link VSRawQueryBuilder.execute} runs — so a builder can
|
|
314
|
+
* be created before a transaction and pointed at it from inside, or a
|
|
315
|
+
* {@link VSRawQueryBuilder.clone} can be pointed at another client without touching the
|
|
316
|
+
* original builder.
|
|
317
|
+
*
|
|
318
|
+
* @example
|
|
319
|
+
* ```typescript
|
|
320
|
+
* const qb = orderRepository.createRawQueryBuilder().select("*").from("order");
|
|
321
|
+
*
|
|
322
|
+
* await orderRepository.transaction(async tx => {
|
|
323
|
+
* qb.setDb(tx);
|
|
324
|
+
* return qb.execute();
|
|
325
|
+
* });
|
|
326
|
+
* ```
|
|
327
|
+
*/
|
|
328
|
+
setDb(db) {
|
|
329
|
+
this.db = db;
|
|
330
|
+
this.trace("db replaced");
|
|
331
|
+
}
|
|
332
|
+
/**
|
|
333
|
+
* Returns an independent builder with the same clauses and `db`. Changes made to either one
|
|
334
|
+
* afterwards don't affect the other (the underlying `VSSql`/`VSRawQueryBuilder` values
|
|
335
|
+
* themselves are immutable, so a shallow copy of every clause array is enough).
|
|
336
|
+
*/
|
|
337
|
+
clone() {
|
|
338
|
+
const copy = new VSRawQueryBuilder(this.db, this.adapter, this.logger);
|
|
339
|
+
copy.recursiveWith = this.recursiveWith;
|
|
340
|
+
copy.cteClauses = [...this.cteClauses];
|
|
341
|
+
copy.selectColumns = [...this.selectColumns];
|
|
342
|
+
copy.fromTarget = this.fromTarget;
|
|
343
|
+
copy.joinClauses = [...this.joinClauses];
|
|
344
|
+
copy.whereConditions = [...this.whereConditions];
|
|
345
|
+
copy.groupByColumns = [...this.groupByColumns];
|
|
346
|
+
copy.havingConditions = [...this.havingConditions];
|
|
347
|
+
copy.orderByClauses = [...this.orderByClauses];
|
|
348
|
+
copy.limitValue = this.limitValue;
|
|
349
|
+
copy.offsetValue = this.offsetValue;
|
|
350
|
+
this.trace("clone");
|
|
351
|
+
return copy;
|
|
352
|
+
}
|
|
353
|
+
compileCteClause() {
|
|
354
|
+
const ctes = this.cteClauses.map(({ name, columns, query }) => {
|
|
355
|
+
const columnList = columns?.length ? vs_sql_util_1.VSSql.sql `(${vs_sql_util_1.VSSql.raw(columns.join(", "))}) ` : vs_sql_util_1.VSSql.empty;
|
|
356
|
+
return vs_sql_util_1.VSSql.sql `${vs_sql_util_1.VSSql.raw(name)} ${columnList}AS (${query})`;
|
|
357
|
+
});
|
|
358
|
+
return vs_sql_util_1.VSSql.sql `WITH ${this.recursiveWith ? vs_sql_util_1.VSSql.raw("RECURSIVE ") : vs_sql_util_1.VSSql.empty}${vs_sql_util_1.VSSql.join(ctes, ", ")}`;
|
|
359
|
+
}
|
|
360
|
+
/**
|
|
361
|
+
* Compiles every configured clause into a single {@link VSSql} fragment, in the order
|
|
362
|
+
* `WITH` (CTEs) -> `SELECT` -> `FROM` -> `JOIN`s -> `WHERE` -> `GROUP BY` -> `HAVING` -> `ORDER BY`
|
|
363
|
+
* -> `LIMIT` -> `OFFSET`. Nothing runs by itself — splice the result into another `VSSql`
|
|
364
|
+
* fragment as a subquery, or pass it to `VSRepository.query()`.
|
|
365
|
+
*/
|
|
366
|
+
toVSSql() {
|
|
367
|
+
if (!this.fromTarget) {
|
|
368
|
+
throw new VSRepoError_1.VSRepoError("'from' is required before calling 'toVSSql'", vsrepo_error_type_enum_1.VSRepoErrorType.QUERY_BUILDER);
|
|
369
|
+
}
|
|
370
|
+
const select = this.selectColumns.length ? vs_sql_util_1.VSSql.join(this.selectColumns, ", ") : vs_sql_util_1.VSSql.raw("*");
|
|
371
|
+
let query = vs_sql_util_1.VSSql.sql `SELECT ${select} FROM ${this.fromTarget}`;
|
|
372
|
+
for (const { type, target, on } of this.joinClauses) {
|
|
373
|
+
query = vs_sql_util_1.VSSql.sql `${query} ${vs_sql_util_1.VSSql.raw(`${type} JOIN`)} ${target} ON ${on}`;
|
|
374
|
+
}
|
|
375
|
+
if (this.whereConditions.length) {
|
|
376
|
+
query = vs_sql_util_1.VSSql.sql `${query} WHERE ${VSRawQueryBuilder.combine(this.whereConditions)}`;
|
|
377
|
+
}
|
|
378
|
+
if (this.groupByColumns.length) {
|
|
379
|
+
query = vs_sql_util_1.VSSql.sql `${query} GROUP BY ${vs_sql_util_1.VSSql.join(this.groupByColumns, ", ")}`;
|
|
380
|
+
}
|
|
381
|
+
if (this.havingConditions.length) {
|
|
382
|
+
query = vs_sql_util_1.VSSql.sql `${query} HAVING ${VSRawQueryBuilder.combine(this.havingConditions)}`;
|
|
383
|
+
}
|
|
384
|
+
if (this.orderByClauses.length) {
|
|
385
|
+
query = vs_sql_util_1.VSSql.sql `${query} ORDER BY ${vs_sql_util_1.VSSql.join(this.orderByClauses, ", ")}`;
|
|
386
|
+
}
|
|
387
|
+
if (this.limitValue !== undefined) {
|
|
388
|
+
query = vs_sql_util_1.VSSql.sql `${query} LIMIT ${this.limitValue}`;
|
|
389
|
+
}
|
|
390
|
+
if (this.offsetValue !== undefined) {
|
|
391
|
+
query = vs_sql_util_1.VSSql.sql `${query} OFFSET ${this.offsetValue}`;
|
|
392
|
+
}
|
|
393
|
+
if (this.cteClauses.length) {
|
|
394
|
+
query = vs_sql_util_1.VSSql.sql `${this.compileCteClause()} ${query}`;
|
|
395
|
+
}
|
|
396
|
+
return query;
|
|
397
|
+
}
|
|
398
|
+
compileForAdapter() {
|
|
399
|
+
if (!this.adapter.getPlaceholder) {
|
|
400
|
+
throw new VSRepoError_1.VSRepoError("Your adapter did not implement the 'getPlaceholder' method, required to compile a " +
|
|
401
|
+
"'VSRawQueryBuilder'; try updating your adapter to a newer version.", vsrepo_error_type_enum_1.VSRepoErrorType.QUERY_BUILDER);
|
|
402
|
+
}
|
|
403
|
+
return this.toVSSql().compile(index => this.adapter.getPlaceholder(index));
|
|
404
|
+
}
|
|
405
|
+
/**
|
|
406
|
+
* Compiles the builder down to a plain SQL string, rendered with the adapter's own
|
|
407
|
+
* placeholder syntax (e.g. `$1`, `$2`, ...). Values themselves are **not** interpolated into
|
|
408
|
+
* the string — use {@link VSRawQueryBuilder.toVSSql} (`.compile()`) or
|
|
409
|
+
* {@link VSRawQueryBuilder.execute} if you also need the parameter values/to run the query.
|
|
410
|
+
*/
|
|
411
|
+
toSql() {
|
|
412
|
+
return this.compileForAdapter().text;
|
|
413
|
+
}
|
|
414
|
+
/**
|
|
415
|
+
* Compiles and runs the query against the underlying database, through the same adapter as
|
|
416
|
+
* every other `VSRepository` method.
|
|
417
|
+
*
|
|
418
|
+
* @template T Shape of the returned rows. Defaults to `any`.
|
|
419
|
+
*/
|
|
420
|
+
async execute() {
|
|
421
|
+
const { text, args } = this.compileForAdapter();
|
|
422
|
+
this.trace("execute", { query: text, args });
|
|
423
|
+
const start = this.logger?.startPerformLog("run raw query builder execute");
|
|
424
|
+
try {
|
|
425
|
+
return await this.adapter.query(text, { args, db: this.db, modifying: false });
|
|
426
|
+
}
|
|
427
|
+
finally {
|
|
428
|
+
this.logger?.endPerformLog(start);
|
|
429
|
+
}
|
|
430
|
+
}
|
|
431
|
+
}
|
|
432
|
+
exports.VSRawQueryBuilder = VSRawQueryBuilder;
|
|
433
|
+
//# sourceMappingURL=vs-raw-query-builder.util.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"vs-raw-query-builder.util.js","sourceRoot":"","sources":["../../../src/internal/utils/vs-raw-query-builder.util.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1,100 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* An ORM-agnostic, composable SQL fragment that compiles down to whatever placeholder
|
|
3
|
+
* syntax your adapter declares via `VSRepoAdapter.getPlaceholder()`, instead of assuming one.
|
|
4
|
+
*
|
|
5
|
+
* A `VSSql` never touches the database by itself — build one with `VSSql.sql`
|
|
6
|
+
* (or compose it from `VSSql.raw`/`VSSql.join`/`VSSql.empty`) and pass it
|
|
7
|
+
* straight to `VSRepository.query()`, which compiles and executes it.
|
|
8
|
+
*
|
|
9
|
+
* Values interpolated with `${...}` are always sent as parameters. Nesting
|
|
10
|
+
* another `VSSql` inline splices its text and parameters into the outer
|
|
11
|
+
* fragment, so fragments compose freely:
|
|
12
|
+
*
|
|
13
|
+
* @example
|
|
14
|
+
* ```typescript
|
|
15
|
+
* import { VSSql } from "vsrepo";
|
|
16
|
+
*
|
|
17
|
+
* const filter = onlyActive ? VSSql.sql`AND active = ${true}` : VSSql.empty;
|
|
18
|
+
*
|
|
19
|
+
* const fragment = VSSql.sql`
|
|
20
|
+
* SELECT * FROM ${VSSql.raw('"user"')}
|
|
21
|
+
* WHERE id IN (${VSSql.join(ids)})
|
|
22
|
+
* ${filter}
|
|
23
|
+
* `;
|
|
24
|
+
*
|
|
25
|
+
* const users = await userRepository.query<User[]>(fragment);
|
|
26
|
+
* ```
|
|
27
|
+
*
|
|
28
|
+
* @publicApi
|
|
29
|
+
*/
|
|
30
|
+
export declare class VSSql {
|
|
31
|
+
private readonly chunks;
|
|
32
|
+
private readonly values;
|
|
33
|
+
private constructor();
|
|
34
|
+
/**
|
|
35
|
+
* Appends `value` to the in-progress `chunks`/`values` pair: a nested
|
|
36
|
+
* `VSSql` is spliced in (its own text merged into the current trailing
|
|
37
|
+
* chunk, its own parameters appended in order), anything else becomes a
|
|
38
|
+
* single new parameter. `chunks` always has `values.length + 1` entries.
|
|
39
|
+
*/
|
|
40
|
+
private static appendValue;
|
|
41
|
+
/**
|
|
42
|
+
* Compiles this fragment into a single SQL string and its positional
|
|
43
|
+
* argument array, rendering each parameter's placeholder with
|
|
44
|
+
* `getPlaceholder(index)` — `index` is the 0-based position of the
|
|
45
|
+
* parameter in the returned `args` array. Used internally by
|
|
46
|
+
* `VSRepository.query()`; only call this directly if you need the raw
|
|
47
|
+
* `{ text, args }` pair for something else (e.g. logging).
|
|
48
|
+
*/
|
|
49
|
+
compile(getPlaceholder: (index: number) => string): {
|
|
50
|
+
text: string;
|
|
51
|
+
args: unknown[];
|
|
52
|
+
};
|
|
53
|
+
/**
|
|
54
|
+
* Builds a `VSSql` fragment from a tagged template literal. Every
|
|
55
|
+
* interpolated `${value}` becomes a parameter, except another `VSSql`
|
|
56
|
+
* (e.g. from `raw()`, `join()`, or a nested `sql` fragment), which is
|
|
57
|
+
* spliced in as text + its own parameters instead.
|
|
58
|
+
*
|
|
59
|
+
* @example
|
|
60
|
+
* ```typescript
|
|
61
|
+
* const fragment = VSSql.sql`SELECT * FROM "user" WHERE email = ${email}`;
|
|
62
|
+
* const users = await userRepository.query<User[]>(fragment);
|
|
63
|
+
* ```
|
|
64
|
+
*/
|
|
65
|
+
static sql(strings: TemplateStringsArray, ...values: unknown[]): VSSql;
|
|
66
|
+
/**
|
|
67
|
+
* Inserts `text` into the query as-is, unparameterized — for identifiers
|
|
68
|
+
* (table/column names) or SQL keywords that can't be bound as a
|
|
69
|
+
* parameter. `text` is never escaped or validated: only pass trusted,
|
|
70
|
+
* non-user-controlled input.
|
|
71
|
+
*
|
|
72
|
+
* @example
|
|
73
|
+
* ```typescript
|
|
74
|
+
* const table = VSSql.raw('"user"');
|
|
75
|
+
* const fragment = VSSql.sql`SELECT * FROM ${table} WHERE active = ${true}`;
|
|
76
|
+
* ```
|
|
77
|
+
*/
|
|
78
|
+
static raw(text: string): VSSql;
|
|
79
|
+
/**
|
|
80
|
+
* An empty fragment — contributes no text and no parameters. Useful as
|
|
81
|
+
* the "nothing" branch when conditionally composing a fragment.
|
|
82
|
+
* @example
|
|
83
|
+
* ```typescript
|
|
84
|
+
* condition ? VSSql.sql`AND active = true` : VSSql.empty
|
|
85
|
+
* ```
|
|
86
|
+
*/
|
|
87
|
+
static readonly empty: VSSql;
|
|
88
|
+
/**
|
|
89
|
+
* Joins `values` into a single fragment, separated by `separator` and
|
|
90
|
+
* wrapped by `prefix`/`suffix`. Each element is either a parameter value
|
|
91
|
+
* (auto-wrapped, like a normal `${...}` interpolation) or a nested `VSSql`
|
|
92
|
+
* fragment (spliced in). Most commonly used to build an `IN (...)` list.
|
|
93
|
+
*
|
|
94
|
+
* @example
|
|
95
|
+
* ```typescript
|
|
96
|
+
* const fragment = VSSql.sql`WHERE id IN (${VSSql.join(ids)})`;
|
|
97
|
+
* ```
|
|
98
|
+
*/
|
|
99
|
+
static join(values: readonly unknown[], separator?: string, prefix?: string, suffix?: string): VSSql;
|
|
100
|
+
}
|