linkgress-orm 0.4.68 → 0.4.70
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/dist/database/database-client.interface.d.ts +12 -0
- package/dist/database/database-client.interface.d.ts.map +1 -1
- package/dist/database/database-client.interface.js.map +1 -1
- package/dist/database/postgres-client.d.ts.map +1 -1
- package/dist/database/postgres-client.js +21 -9
- package/dist/database/postgres-client.js.map +1 -1
- package/dist/entity/db-context.d.ts +81 -5
- package/dist/entity/db-context.d.ts.map +1 -1
- package/dist/entity/db-context.js +94 -25
- package/dist/entity/db-context.js.map +1 -1
- package/dist/query/cte-builder.d.ts.map +1 -1
- package/dist/query/cte-builder.js +3 -2
- package/dist/query/cte-builder.js.map +1 -1
- package/dist/query/mock-row-cache.d.ts +9 -8
- package/dist/query/mock-row-cache.d.ts.map +1 -1
- package/dist/query/mock-row-cache.js +9 -8
- package/dist/query/mock-row-cache.js.map +1 -1
- package/dist/query/mutation-batch.d.ts.map +1 -1
- package/dist/query/mutation-batch.js +3 -1
- package/dist/query/mutation-batch.js.map +1 -1
- package/dist/query/query-builder.d.ts +36 -0
- package/dist/query/query-builder.d.ts.map +1 -1
- package/dist/query/query-builder.js +346 -207
- package/dist/query/query-builder.js.map +1 -1
- package/dist/query/union-builder.d.ts.map +1 -1
- package/dist/query/union-builder.js +2 -1
- package/dist/query/union-builder.js.map +1 -1
- package/package.json +80 -80
|
@@ -268,23 +268,33 @@ class QueryExecutor {
|
|
|
268
268
|
* `.expectedExecutionTime()`. If the query runs longer than this,
|
|
269
269
|
* `onQueryTakingTooLong` fires. `undefined` means use the context default.
|
|
270
270
|
*/
|
|
271
|
-
overrideExpectedMs
|
|
271
|
+
overrideExpectedMs,
|
|
272
|
+
/**
|
|
273
|
+
* Per-query prepared-statement override set via `.withPreparedStatements()`.
|
|
274
|
+
* `undefined` means use the context's `preparedStatements` default.
|
|
275
|
+
*/
|
|
276
|
+
overridePrepare) {
|
|
272
277
|
this.client = client;
|
|
273
278
|
this.options = options;
|
|
274
279
|
this.overrideTimeoutMs = overrideTimeoutMs;
|
|
275
280
|
this.overrideExpectedMs = overrideExpectedMs;
|
|
281
|
+
this.overridePrepare = overridePrepare;
|
|
276
282
|
}
|
|
277
283
|
/**
|
|
278
|
-
* Build the per-query execution options (binary protocol
|
|
279
|
-
* or `undefined` when
|
|
284
|
+
* Build the per-query execution options (binary protocol, timeout override, prepared
|
|
285
|
+
* statement), or `undefined` when none is set so the driver takes its fast path.
|
|
286
|
+
* `execution` is a per-call override a caller passes when it knows the statement's
|
|
287
|
+
* text is unique to this call (see {@link QueryOptions.preparedStatements}).
|
|
280
288
|
*/
|
|
281
|
-
buildExecutionOptions() {
|
|
282
|
-
|
|
289
|
+
buildExecutionOptions(execution) {
|
|
290
|
+
const prepare = execution?.prepare ?? this.overridePrepare ?? this.options.preparedStatements;
|
|
291
|
+
if (!this.options.useBinaryProtocol && this.overrideTimeoutMs === undefined && prepare !== true) {
|
|
283
292
|
return undefined;
|
|
284
293
|
}
|
|
285
294
|
return {
|
|
286
295
|
useBinaryProtocol: this.options.useBinaryProtocol,
|
|
287
296
|
timeoutMs: this.overrideTimeoutMs,
|
|
297
|
+
...(prepare === true ? { prepare: true } : {}),
|
|
288
298
|
};
|
|
289
299
|
}
|
|
290
300
|
/**
|
|
@@ -293,7 +303,7 @@ class QueryExecutor {
|
|
|
293
303
|
* the derived executor. Used by `.withTimeout()` on the query builders.
|
|
294
304
|
*/
|
|
295
305
|
withTimeout(timeoutMs) {
|
|
296
|
-
return new QueryExecutor(this.client, this.options, timeoutMs, this.overrideExpectedMs);
|
|
306
|
+
return new QueryExecutor(this.client, this.options, timeoutMs, this.overrideExpectedMs, this.overridePrepare);
|
|
297
307
|
}
|
|
298
308
|
/**
|
|
299
309
|
* Return a new executor that flags this query as expected to finish within
|
|
@@ -301,7 +311,16 @@ class QueryExecutor {
|
|
|
301
311
|
* `.expectedExecutionTime()` on the query builders.
|
|
302
312
|
*/
|
|
303
313
|
withExpectedExecutionTime(expectedMs) {
|
|
304
|
-
return new QueryExecutor(this.client, this.options, this.overrideTimeoutMs, expectedMs);
|
|
314
|
+
return new QueryExecutor(this.client, this.options, this.overrideTimeoutMs, expectedMs, this.overridePrepare);
|
|
315
|
+
}
|
|
316
|
+
/**
|
|
317
|
+
* Return a new executor that runs its statements as named server-side prepared
|
|
318
|
+
* statements (`true`) or as unnamed statements (`false`), overriding the context's
|
|
319
|
+
* `preparedStatements` default. Used by `.withPreparedStatements()` on tables and
|
|
320
|
+
* accessors.
|
|
321
|
+
*/
|
|
322
|
+
withPreparedStatements(prepare) {
|
|
323
|
+
return new QueryExecutor(this.client, this.options, this.overrideTimeoutMs, this.overrideExpectedMs, prepare);
|
|
305
324
|
}
|
|
306
325
|
/** Whether slow-query detection is active (a callback is configured). */
|
|
307
326
|
get slowQueryEnabled() {
|
|
@@ -341,7 +360,7 @@ class QueryExecutor {
|
|
|
341
360
|
fireSlowQueryCallback(callback, sql, params, duration, this.expectedExecutionMs, timing.stackHolder);
|
|
342
361
|
}
|
|
343
362
|
}
|
|
344
|
-
async query(sql, params) {
|
|
363
|
+
async query(sql, params, execution) {
|
|
345
364
|
const logger = this.options.logger || defaultLogger;
|
|
346
365
|
const timing = this.beginTiming();
|
|
347
366
|
if (this.options.logQueries) {
|
|
@@ -352,14 +371,12 @@ class QueryExecutor {
|
|
|
352
371
|
}
|
|
353
372
|
}
|
|
354
373
|
try {
|
|
355
|
-
const result = await this.client.query(sql, params, this.buildExecutionOptions());
|
|
374
|
+
const result = await this.client.query(sql, params, this.buildExecutionOptions(execution));
|
|
356
375
|
this.finishTiming(timing, logger, sql, params);
|
|
357
376
|
return result;
|
|
358
377
|
}
|
|
359
378
|
catch (error) {
|
|
360
|
-
|
|
361
|
-
logger(`[SQL Error] ${error instanceof Error ? error.message : String(error)}`, 'error');
|
|
362
|
-
}
|
|
379
|
+
this.logFailure(logger, error, sql, params);
|
|
363
380
|
throw error;
|
|
364
381
|
}
|
|
365
382
|
}
|
|
@@ -387,9 +404,7 @@ class QueryExecutor {
|
|
|
387
404
|
return result;
|
|
388
405
|
}
|
|
389
406
|
catch (error) {
|
|
390
|
-
|
|
391
|
-
logger(`[SQL Error] ${error instanceof Error ? error.message : String(error)}`, 'error');
|
|
392
|
-
}
|
|
407
|
+
this.logFailure(logger, error, sql);
|
|
393
408
|
throw error;
|
|
394
409
|
}
|
|
395
410
|
}
|
|
@@ -416,12 +431,36 @@ class QueryExecutor {
|
|
|
416
431
|
}
|
|
417
432
|
}
|
|
418
433
|
catch (error) {
|
|
419
|
-
|
|
420
|
-
logger(`[SQL Error] ${error instanceof Error ? error.message : String(error)}`, 'error');
|
|
421
|
-
}
|
|
434
|
+
this.logFailure(logger, error, sql);
|
|
422
435
|
throw error;
|
|
423
436
|
}
|
|
424
437
|
}
|
|
438
|
+
/**
|
|
439
|
+
* Whether these options call for an executor at all — any logging, failure reporting,
|
|
440
|
+
* timing or slow-query duty. A context without any of them talks to the client directly.
|
|
441
|
+
*/
|
|
442
|
+
static isNeeded(options) {
|
|
443
|
+
return !!options && !!(options.logQueries
|
|
444
|
+
|| options.logFailedQueries
|
|
445
|
+
|| options.logExecutionTime
|
|
446
|
+
|| options.onQueryTakingTooLong
|
|
447
|
+
|| options.preparedStatements);
|
|
448
|
+
}
|
|
449
|
+
/**
|
|
450
|
+
* The `[SQL Error]` line for a failed statement: the driver's message, the statement text
|
|
451
|
+
* and — only while `logParameters` is on — the parameters. Gated on `logFailedQueries`,
|
|
452
|
+
* which defaults to `logQueries` (see {@link QueryOptions.logFailedQueries}).
|
|
453
|
+
*/
|
|
454
|
+
logFailure(logger, error, sql, params) {
|
|
455
|
+
if (!(this.options.logFailedQueries ?? this.options.logQueries)) {
|
|
456
|
+
return;
|
|
457
|
+
}
|
|
458
|
+
const message = error instanceof Error ? error.message : String(error);
|
|
459
|
+
const parameters = this.options.logParameters && params && params.length > 0
|
|
460
|
+
? `\n[Parameters] ${JSON.stringify(params)}`
|
|
461
|
+
: '';
|
|
462
|
+
logger(`[SQL Error] ${message}\n${sql.trim()}${parameters}`, 'error');
|
|
463
|
+
}
|
|
425
464
|
/**
|
|
426
465
|
* Get the query options for this executor
|
|
427
466
|
*/
|
|
@@ -637,7 +676,7 @@ class TableAccessor {
|
|
|
637
676
|
const mergedStrategy = options.collectionStrategy ?? this.collectionStrategy;
|
|
638
677
|
// Create new executor if logging options are provided
|
|
639
678
|
let newExecutor = this.executor;
|
|
640
|
-
if (
|
|
679
|
+
if (QueryExecutor.isNeeded(options)) {
|
|
641
680
|
newExecutor = new QueryExecutor(this.client, {
|
|
642
681
|
...options,
|
|
643
682
|
collectionStrategy: mergedStrategy,
|
|
@@ -661,6 +700,20 @@ class TableAccessor {
|
|
|
661
700
|
: new QueryExecutor(this.client, undefined, timeoutMs);
|
|
662
701
|
return new TableAccessor(this.tableBuilder, this.client, this.schemaRegistry, newExecutor, this.collectionStrategy);
|
|
663
702
|
}
|
|
703
|
+
/**
|
|
704
|
+
* Run every query started from the returned accessor as a named server-side prepared
|
|
705
|
+
* statement (`true`) or as an unnamed statement (`false`), overriding the context's
|
|
706
|
+
* `preparedStatements` default. See {@link QueryOptions.preparedStatements}.
|
|
707
|
+
*
|
|
708
|
+
* @example
|
|
709
|
+
* await db.users.withPreparedStatements(false).where(u => gt(u.id, 0)).toList();
|
|
710
|
+
*/
|
|
711
|
+
withPreparedStatements(prepare) {
|
|
712
|
+
const newExecutor = this.executor
|
|
713
|
+
? this.executor.withPreparedStatements(prepare)
|
|
714
|
+
: new QueryExecutor(this.client, undefined, undefined, undefined, prepare);
|
|
715
|
+
return new TableAccessor(this.tableBuilder, this.client, this.schemaRegistry, newExecutor, this.collectionStrategy);
|
|
716
|
+
}
|
|
664
717
|
/**
|
|
665
718
|
* Mark queries started from the returned accessor as expected to finish within
|
|
666
719
|
* `expectedMs` (ms). If a query runs longer, the context's
|
|
@@ -984,7 +1037,7 @@ class DataContext {
|
|
|
984
1037
|
this.client = client;
|
|
985
1038
|
this.queryOptions = queryOptions;
|
|
986
1039
|
// Create executor if logging is enabled
|
|
987
|
-
if (queryOptions
|
|
1040
|
+
if (QueryExecutor.isNeeded(queryOptions)) {
|
|
988
1041
|
this.executor = new QueryExecutor(client, queryOptions);
|
|
989
1042
|
}
|
|
990
1043
|
this.initializeSchema(schema);
|
|
@@ -1141,7 +1194,7 @@ class DataContext {
|
|
|
1141
1194
|
: this.queryOptions;
|
|
1142
1195
|
txContext.queryOptions = effectiveOptions;
|
|
1143
1196
|
// Create executor for the transactional client if logging is enabled
|
|
1144
|
-
if (effectiveOptions
|
|
1197
|
+
if (QueryExecutor.isNeeded(effectiveOptions)) {
|
|
1145
1198
|
txContext.executor = new QueryExecutor(txClient, effectiveOptions);
|
|
1146
1199
|
}
|
|
1147
1200
|
// Create fresh table accessors bound to the transactional client
|
|
@@ -1484,7 +1537,7 @@ class DbEntityTable {
|
|
|
1484
1537
|
const mergedOptions = { ...originalOptions, ...options };
|
|
1485
1538
|
// Create new executor if logging options are provided
|
|
1486
1539
|
let newExecutor = originalContext.executor;
|
|
1487
|
-
if (
|
|
1540
|
+
if (QueryExecutor.isNeeded(mergedOptions)) {
|
|
1488
1541
|
newExecutor = new QueryExecutor(originalContext.client, mergedOptions);
|
|
1489
1542
|
}
|
|
1490
1543
|
// Create a proxy context that overrides queryOptions, executor, and getTable
|
|
@@ -1532,6 +1585,19 @@ class DbEntityTable {
|
|
|
1532
1585
|
withTimeout(timeoutMs) {
|
|
1533
1586
|
return this._deriveWithExecutor((current, client) => current ? current.withTimeout(timeoutMs) : new QueryExecutor(client, undefined, timeoutMs));
|
|
1534
1587
|
}
|
|
1588
|
+
/**
|
|
1589
|
+
* Run every query and CRUD operation started from the returned table as a named
|
|
1590
|
+
* server-side prepared statement (`true`) or as an unnamed statement (`false`),
|
|
1591
|
+
* overriding the context's `preparedStatements` default. Use `false` to keep a wide
|
|
1592
|
+
* analytical query on custom plans inside a prepared context, `true` to prepare a
|
|
1593
|
+
* single hot lookup on an unprepared one. See {@link QueryOptions.preparedStatements}.
|
|
1594
|
+
*
|
|
1595
|
+
* @example
|
|
1596
|
+
* await db.products.withPreparedStatements(false).where(p => eq(p.active, true)).toList();
|
|
1597
|
+
*/
|
|
1598
|
+
withPreparedStatements(prepare) {
|
|
1599
|
+
return this._deriveWithExecutor((current, client) => current ? current.withPreparedStatements(prepare) : new QueryExecutor(client, undefined, undefined, undefined, prepare));
|
|
1600
|
+
}
|
|
1535
1601
|
/**
|
|
1536
1602
|
* Mark queries started from the returned table as expected to finish within
|
|
1537
1603
|
* `expectedMs` (ms). If a query runs longer, the context's
|
|
@@ -2168,7 +2234,8 @@ ${parentSql}
|
|
|
2168
2234
|
'__iwc_parent__',
|
|
2169
2235
|
]]),
|
|
2170
2236
|
});
|
|
2171
|
-
|
|
2237
|
+
// Per-call VALUES list ⇒ unique text ⇒ never a prepared statement (see QueryOptions.preparedStatements).
|
|
2238
|
+
const result = executor ? await executor.query(built.sql, built.params, { prepare: false }) : await client.query(built.sql, built.params);
|
|
2172
2239
|
rawRows = result.rows;
|
|
2173
2240
|
mapChildren = stripped => childTable.mapReturningResultsWithNavigation(stripped, navigationInfo.navigationFields, built.nestedPaths);
|
|
2174
2241
|
}
|
|
@@ -2184,7 +2251,8 @@ RETURNING ${returningClause.sql}${pkExtra}
|
|
|
2184
2251
|
SELECT "__mutation__".*, ${extraSelects.join(', ')}
|
|
2185
2252
|
FROM "__mutation__"
|
|
2186
2253
|
${extraJoins.join('\n')}${orderBy}`;
|
|
2187
|
-
|
|
2254
|
+
// Per-call VALUES list ⇒ unique text ⇒ never a prepared statement (see QueryOptions.preparedStatements).
|
|
2255
|
+
const result = executor ? await executor.query(sql, params, { prepare: false }) : await client.query(sql, params);
|
|
2188
2256
|
rawRows = result.rows;
|
|
2189
2257
|
mapChildren = stripped => childTable.mapReturningResults(stripped.map(({ __iwc_child_pk__: _pk, ...rest }) => rest), returningClause.aliasToProperty);
|
|
2190
2258
|
}
|
|
@@ -2361,7 +2429,8 @@ SELECT "__mutation__".*, ${parentJoinSelects.join(', ')}
|
|
|
2361
2429
|
FROM "__mutation__"
|
|
2362
2430
|
JOIN "__ibwc_pord__" "__ibwc_pj__" ON "__ibwc_pj__"."${parentPkDbName}" = "__mutation__"."__ibwc_child_fk__"
|
|
2363
2431
|
ORDER BY "__mutation__"."__ibwc_child_pk__"`;
|
|
2364
|
-
|
|
2432
|
+
// Per-call VALUES lists ⇒ unique text ⇒ never a prepared statement (see QueryOptions.preparedStatements).
|
|
2433
|
+
const result = executor ? await executor.query(sql, params, { prepare: false }) : await client.query(sql, params);
|
|
2365
2434
|
const rawRows = result.rows;
|
|
2366
2435
|
const parentsByOrd = new Map();
|
|
2367
2436
|
const strippedRows = [];
|