linkgress-orm 0.4.69 → 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.
@@ -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 + timeout override),
279
- * or `undefined` when neither is set so the driver takes its fast path.
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
- if (!this.options.useBinaryProtocol && this.overrideTimeoutMs === undefined) {
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,7 +371,7 @@ 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
  }
@@ -421,7 +440,11 @@ class QueryExecutor {
421
440
  * timing or slow-query duty. A context without any of them talks to the client directly.
422
441
  */
423
442
  static isNeeded(options) {
424
- return !!options && !!(options.logQueries || options.logFailedQueries || options.logExecutionTime || options.onQueryTakingTooLong);
443
+ return !!options && !!(options.logQueries
444
+ || options.logFailedQueries
445
+ || options.logExecutionTime
446
+ || options.onQueryTakingTooLong
447
+ || options.preparedStatements);
425
448
  }
426
449
  /**
427
450
  * The `[SQL Error]` line for a failed statement: the driver's message, the statement text
@@ -677,6 +700,20 @@ class TableAccessor {
677
700
  : new QueryExecutor(this.client, undefined, timeoutMs);
678
701
  return new TableAccessor(this.tableBuilder, this.client, this.schemaRegistry, newExecutor, this.collectionStrategy);
679
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
+ }
680
717
  /**
681
718
  * Mark queries started from the returned accessor as expected to finish within
682
719
  * `expectedMs` (ms). If a query runs longer, the context's
@@ -1548,6 +1585,19 @@ class DbEntityTable {
1548
1585
  withTimeout(timeoutMs) {
1549
1586
  return this._deriveWithExecutor((current, client) => current ? current.withTimeout(timeoutMs) : new QueryExecutor(client, undefined, timeoutMs));
1550
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
+ }
1551
1601
  /**
1552
1602
  * Mark queries started from the returned table as expected to finish within
1553
1603
  * `expectedMs` (ms). If a query runs longer, the context's
@@ -2184,7 +2234,8 @@ ${parentSql}
2184
2234
  '__iwc_parent__',
2185
2235
  ]]),
2186
2236
  });
2187
- const result = executor ? await executor.query(built.sql, built.params) : await client.query(built.sql, built.params);
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);
2188
2239
  rawRows = result.rows;
2189
2240
  mapChildren = stripped => childTable.mapReturningResultsWithNavigation(stripped, navigationInfo.navigationFields, built.nestedPaths);
2190
2241
  }
@@ -2200,7 +2251,8 @@ RETURNING ${returningClause.sql}${pkExtra}
2200
2251
  SELECT "__mutation__".*, ${extraSelects.join(', ')}
2201
2252
  FROM "__mutation__"
2202
2253
  ${extraJoins.join('\n')}${orderBy}`;
2203
- const result = executor ? await executor.query(sql, params) : await client.query(sql, params);
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);
2204
2256
  rawRows = result.rows;
2205
2257
  mapChildren = stripped => childTable.mapReturningResults(stripped.map(({ __iwc_child_pk__: _pk, ...rest }) => rest), returningClause.aliasToProperty);
2206
2258
  }
@@ -2377,7 +2429,8 @@ SELECT "__mutation__".*, ${parentJoinSelects.join(', ')}
2377
2429
  FROM "__mutation__"
2378
2430
  JOIN "__ibwc_pord__" "__ibwc_pj__" ON "__ibwc_pj__"."${parentPkDbName}" = "__mutation__"."__ibwc_child_fk__"
2379
2431
  ORDER BY "__mutation__"."__ibwc_child_pk__"`;
2380
- const result = executor ? await executor.query(sql, params) : await client.query(sql, params);
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);
2381
2434
  const rawRows = result.rows;
2382
2435
  const parentsByOrd = new Map();
2383
2436
  const strippedRows = [];