forge-sql-orm 2.1.11 → 2.1.13

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.
Files changed (73) hide show
  1. package/README.md +800 -541
  2. package/dist/core/ForgeSQLAnalyseOperations.d.ts.map +1 -1
  3. package/dist/core/ForgeSQLAnalyseOperations.js +257 -0
  4. package/dist/core/ForgeSQLAnalyseOperations.js.map +1 -0
  5. package/dist/core/ForgeSQLCacheOperations.js +172 -0
  6. package/dist/core/ForgeSQLCacheOperations.js.map +1 -0
  7. package/dist/core/ForgeSQLCrudOperations.js +349 -0
  8. package/dist/core/ForgeSQLCrudOperations.js.map +1 -0
  9. package/dist/core/ForgeSQLORM.d.ts +1 -1
  10. package/dist/core/ForgeSQLORM.d.ts.map +1 -1
  11. package/dist/core/ForgeSQLORM.js +1191 -0
  12. package/dist/core/ForgeSQLORM.js.map +1 -0
  13. package/dist/core/ForgeSQLQueryBuilder.js +77 -0
  14. package/dist/core/ForgeSQLQueryBuilder.js.map +1 -0
  15. package/dist/core/ForgeSQLSelectOperations.js +81 -0
  16. package/dist/core/ForgeSQLSelectOperations.js.map +1 -0
  17. package/dist/core/SystemTables.js +258 -0
  18. package/dist/core/SystemTables.js.map +1 -0
  19. package/dist/index.js +30 -0
  20. package/dist/index.js.map +1 -0
  21. package/dist/lib/drizzle/extensions/additionalActions.d.ts.map +1 -1
  22. package/dist/lib/drizzle/extensions/additionalActions.js +527 -0
  23. package/dist/lib/drizzle/extensions/additionalActions.js.map +1 -0
  24. package/dist/utils/cacheContextUtils.d.ts.map +1 -1
  25. package/dist/utils/cacheContextUtils.js +198 -0
  26. package/dist/utils/cacheContextUtils.js.map +1 -0
  27. package/dist/utils/cacheUtils.d.ts.map +1 -1
  28. package/dist/utils/cacheUtils.js +383 -0
  29. package/dist/utils/cacheUtils.js.map +1 -0
  30. package/dist/utils/forgeDriver.d.ts.map +1 -1
  31. package/dist/utils/forgeDriver.js +139 -0
  32. package/dist/utils/forgeDriver.js.map +1 -0
  33. package/dist/utils/forgeDriverProxy.js +68 -0
  34. package/dist/utils/forgeDriverProxy.js.map +1 -0
  35. package/dist/utils/metadataContextUtils.d.ts.map +1 -1
  36. package/dist/utils/metadataContextUtils.js +28 -0
  37. package/dist/utils/metadataContextUtils.js.map +1 -0
  38. package/dist/utils/requestTypeContextUtils.js +10 -0
  39. package/dist/utils/requestTypeContextUtils.js.map +1 -0
  40. package/dist/utils/sqlHints.js +52 -0
  41. package/dist/utils/sqlHints.js.map +1 -0
  42. package/dist/utils/sqlUtils.d.ts.map +1 -1
  43. package/dist/utils/sqlUtils.js +590 -0
  44. package/dist/utils/sqlUtils.js.map +1 -0
  45. package/dist/webtriggers/applyMigrationsWebTrigger.js +77 -0
  46. package/dist/webtriggers/applyMigrationsWebTrigger.js.map +1 -0
  47. package/dist/webtriggers/clearCacheSchedulerTrigger.js +83 -0
  48. package/dist/webtriggers/clearCacheSchedulerTrigger.js.map +1 -0
  49. package/dist/webtriggers/dropMigrationWebTrigger.js +54 -0
  50. package/dist/webtriggers/dropMigrationWebTrigger.js.map +1 -0
  51. package/dist/webtriggers/dropTablesMigrationWebTrigger.js +54 -0
  52. package/dist/webtriggers/dropTablesMigrationWebTrigger.js.map +1 -0
  53. package/dist/webtriggers/fetchSchemaWebTrigger.js +82 -0
  54. package/dist/webtriggers/fetchSchemaWebTrigger.js.map +1 -0
  55. package/dist/webtriggers/index.js +40 -0
  56. package/dist/webtriggers/index.js.map +1 -0
  57. package/dist/webtriggers/slowQuerySchedulerTrigger.d.ts.map +1 -1
  58. package/dist/webtriggers/slowQuerySchedulerTrigger.js +80 -0
  59. package/dist/webtriggers/slowQuerySchedulerTrigger.js.map +1 -0
  60. package/package.json +28 -23
  61. package/src/core/ForgeSQLAnalyseOperations.ts +3 -2
  62. package/src/core/ForgeSQLORM.ts +33 -27
  63. package/src/lib/drizzle/extensions/additionalActions.ts +11 -0
  64. package/src/utils/cacheContextUtils.ts +9 -6
  65. package/src/utils/cacheUtils.ts +28 -5
  66. package/src/utils/forgeDriver.ts +10 -6
  67. package/src/utils/metadataContextUtils.ts +1 -4
  68. package/src/utils/sqlUtils.ts +136 -125
  69. package/src/webtriggers/slowQuerySchedulerTrigger.ts +40 -33
  70. package/dist/ForgeSQLORM.js +0 -3896
  71. package/dist/ForgeSQLORM.js.map +0 -1
  72. package/dist/ForgeSQLORM.mjs +0 -3879
  73. package/dist/ForgeSQLORM.mjs.map +0 -1
@@ -3,11 +3,11 @@ import {
3
3
  AnyColumn,
4
4
  Column,
5
5
  gte,
6
- ilike,
6
+ ilike,
7
7
  isNotNull,
8
8
  isTable,
9
- ne,
10
- not,
9
+ ne,
10
+ not,
11
11
  notInArray,
12
12
  SQL,
13
13
  sql,
@@ -20,16 +20,14 @@ import { AnyIndexBuilder } from "drizzle-orm/mysql-core/indexes";
20
20
  import { CheckBuilder } from "drizzle-orm/mysql-core/checks";
21
21
  import { ForeignKeyBuilder } from "drizzle-orm/mysql-core/foreign-keys";
22
22
  import { UniqueConstraintBuilder } from "drizzle-orm/mysql-core/unique-constraint";
23
- import {
24
- SelectedFields
25
- } from "drizzle-orm/mysql-core/query-builders/select.types";
23
+ import { SelectedFields } from "drizzle-orm/mysql-core/query-builders/select.types";
26
24
  import { MySqlTable } from "drizzle-orm/mysql-core";
27
25
  import { isSQLWrapper } from "drizzle-orm/sql/sql";
28
- import {clusterStatementsSummary, slowQuery} from "../core/SystemTables";
26
+ import { clusterStatementsSummary, slowQuery } from "../core/SystemTables";
29
27
  import { ForgeSqlOperation } from "../core/ForgeSQLQueryBuilder";
30
- import { ColumnDataType} from "drizzle-orm/column-builder";
31
- import {AnyMySqlColumn} from "drizzle-orm/mysql-core/columns/common";
32
- import type {ColumnBaseConfig} from "drizzle-orm/column";
28
+ import { ColumnDataType } from "drizzle-orm/column-builder";
29
+ import { AnyMySqlColumn } from "drizzle-orm/mysql-core/columns/common";
30
+ import type { ColumnBaseConfig } from "drizzle-orm/column";
33
31
 
34
32
  /**
35
33
  * Interface representing table metadata information
@@ -95,7 +93,7 @@ export const parseDateTime = (value: string | Date, format: string): Date => {
95
93
  }
96
94
  }
97
95
  // 4. Ensure the result is a valid Date object
98
- if (isNaN(result.getTime())) {
96
+ if (Number.isNaN(result.getTime())) {
99
97
  result = new Date(value);
100
98
  }
101
99
  return result;
@@ -129,7 +127,7 @@ export function formatDateTime(
129
127
  }
130
128
  if (!dt?.isValid) {
131
129
  const parsed = Number(value);
132
- if (!isNaN(parsed)) {
130
+ if (!Number.isNaN(parsed)) {
133
131
  dt = DateTime.fromMillis(parsed);
134
132
  }
135
133
  }
@@ -185,17 +183,15 @@ export function getPrimaryKeys<T extends AnyMySqlTable>(table: T): [string, AnyC
185
183
  // Collect all primary key columns from all primary key builders
186
184
  const primaryKeyColumns = new Set<[string, AnyColumn]>();
187
185
 
188
- primaryKeys.forEach((primaryKeyBuilder) => {
186
+ for (const primaryKeyBuilder of primaryKeys) {
189
187
  // Get primary key columns from each builder
190
- Object.entries(columns)
191
- .filter(([, column]) => {
192
- // @ts-ignore - PrimaryKeyBuilder has internal columns property
193
- return primaryKeyBuilder.columns.includes(column);
194
- })
195
- .forEach(([name, column]) => {
196
- primaryKeyColumns.add([name, column]);
197
- });
198
- });
188
+ for (const [name, column1] of Object.entries(columns).filter(([, column]) => {
189
+ // @ts-ignore - PrimaryKeyBuilder has internal columns property
190
+ return primaryKeyBuilder.columns.includes(column);
191
+ })) {
192
+ primaryKeyColumns.add([name, column1]);
193
+ }
194
+ }
199
195
 
200
196
  return Array.from(primaryKeyColumns);
201
197
  }
@@ -222,12 +218,12 @@ function processForeignKeys(
222
218
  // @ts-ignore
223
219
  const fkArray: any[] = table[foreignKeysSymbol];
224
220
  if (fkArray) {
225
- fkArray.forEach((fk) => {
221
+ for (const fk of fkArray) {
226
222
  if (fk.reference) {
227
223
  const item = fk.reference(fk);
228
224
  foreignKeys.push(item);
229
225
  }
230
- });
226
+ }
231
227
  }
232
228
  }
233
229
 
@@ -244,14 +240,14 @@ function processForeignKeys(
244
240
  (item) => (item as ConfigBuilderData).value ?? item,
245
241
  );
246
242
 
247
- configBuilders.forEach((builder) => {
248
- if (!builder?.constructor) return;
243
+ for (const builder of configBuilders) {
244
+ if (!builder?.constructor) continue;
249
245
 
250
246
  const builderName = builder.constructor.name.toLowerCase();
251
247
  if (builderName.includes("foreignkeybuilder")) {
252
248
  foreignKeys.push(builder);
253
249
  }
254
- });
250
+ }
255
251
  }
256
252
  }
257
253
  }
@@ -299,8 +295,8 @@ export function getTableMetadata(table: AnyMySqlTable): MetadataInfo {
299
295
  );
300
296
 
301
297
  // Process each builder
302
- configBuilders.forEach((builder) => {
303
- if (!builder?.constructor) return;
298
+ for (const builder of configBuilders) {
299
+ if (!builder?.constructor) continue;
304
300
 
305
301
  const builderName = builder.constructor.name.toLowerCase();
306
302
 
@@ -322,7 +318,7 @@ export function getTableMetadata(table: AnyMySqlTable): MetadataInfo {
322
318
 
323
319
  // Always add to extras array
324
320
  builders.extras.push(builder);
325
- });
321
+ }
326
322
  }
327
323
  }
328
324
  }
@@ -354,14 +350,14 @@ export function generateDropTableStatements(
354
350
  console.warn('No drop operations requested: both "table" and "sequence" options are false');
355
351
  return [];
356
352
  }
357
- tables.forEach((tableName) => {
353
+ for (const tableName of tables) {
358
354
  if (validOptions.table) {
359
355
  dropStatements.push(`DROP TABLE IF EXISTS \`${tableName}\`;`);
360
356
  }
361
357
  if (validOptions.sequence) {
362
358
  dropStatements.push(`DROP SEQUENCE IF EXISTS \`${tableName}\`;`);
363
359
  }
364
- });
360
+ }
365
361
 
366
362
  return dropStatements;
367
363
  }
@@ -375,13 +371,13 @@ function mapSelectTableToAlias(
375
371
  ): any {
376
372
  const { columns, tableName } = getTableMetadata(table);
377
373
  const selectionsTableFields: Record<string, unknown> = {};
378
- Object.keys(columns).forEach((name) => {
374
+ for (const name of Object.keys(columns)) {
379
375
  const column = columns[name] as AnyColumn;
380
376
  const uniqName = `a_${uniqPrefix}_${tableName}_${column.name}`.toLowerCase();
381
377
  const fieldAlias = sql.raw(uniqName);
382
378
  selectionsTableFields[name] = sql`${column} as \`${fieldAlias}\``;
383
379
  aliasMap[uniqName] = column;
384
- });
380
+ }
385
381
  return selectionsTableFields;
386
382
  }
387
383
 
@@ -408,9 +404,9 @@ export function mapSelectAllFieldsToAlias(
408
404
  selections[name] = fields;
409
405
  } else {
410
406
  const innerSelections: any = {};
411
- Object.entries(fields).forEach(([iname, ifields]) => {
407
+ for (const [iname, ifields] of Object.entries(fields)) {
412
408
  mapSelectAllFieldsToAlias(innerSelections, iname, `${uniqName}_${iname}`, ifields, aliasMap);
413
- });
409
+ }
414
410
  selections[name] = innerSelections;
415
411
  }
416
412
  return selections;
@@ -423,9 +419,10 @@ export function mapSelectFieldsWithAlias<TSelection extends SelectedFields>(
423
419
  }
424
420
  const aliasMap: AliasColumnMap = {};
425
421
  const selections: any = {};
426
- Object.entries(fields).forEach(([name, fields]) => {
427
- mapSelectAllFieldsToAlias(selections, name, name, fields, aliasMap);
428
- });
422
+ for (let i = 0; i < Object.entries(fields).length; i++) {
423
+ const [name, fields1] = Object.entries(fields)[i];
424
+ mapSelectAllFieldsToAlias(selections, name, name, fields1, aliasMap);
425
+ }
429
426
  return { selections, aliasMap };
430
427
  }
431
428
 
@@ -548,7 +545,7 @@ function processNullBranches(obj: Record<string, unknown>): Record<string, unkno
548
545
  }
549
546
 
550
547
  export function formatLimitOffset(limitOrOffset: number): number {
551
- if (typeof limitOrOffset !== "number" || isNaN(limitOrOffset)) {
548
+ if (typeof limitOrOffset !== "number" || Number.isNaN(limitOrOffset)) {
552
549
  throw new Error("limitOrOffset must be a valid number");
553
550
  }
554
551
  return sql.raw(`${limitOrOffset}`) as unknown as number;
@@ -591,8 +588,8 @@ export async function printQueriesWithPlan(
591
588
  ) {
592
589
  try {
593
590
  const statementsTable = clusterStatementsSummary;
594
- const timeoutMs = timeout ?? 3000;
595
- const results = await withTimeout(
591
+ const timeoutMs = timeout ?? 3000;
592
+ const results = await withTimeout(
596
593
  forgeSQLORM
597
594
  .getDrizzleQueryBuilder()
598
595
  .select({
@@ -601,14 +598,21 @@ export async function printQueriesWithPlan(
601
598
  avgMem: statementsTable.avgMem,
602
599
  execCount: statementsTable.execCount,
603
600
  plan: statementsTable.plan,
604
- stmtType: statementsTable.stmtType,
601
+ stmtType: statementsTable.stmtType,
605
602
  })
606
603
  .from(statementsTable)
607
604
  .where(
608
605
  and(
609
606
  isNotNull(statementsTable.digest),
610
- not(ilike(statementsTable.digestText, "%information_schema%")),
611
- notInArray(statementsTable.stmtType, ["Use", "Set", "Show","Commit","Rollback", "Begin"]),
607
+ not(ilike(statementsTable.digestText, "%information_schema%")),
608
+ notInArray(statementsTable.stmtType, [
609
+ "Use",
610
+ "Set",
611
+ "Show",
612
+ "Commit",
613
+ "Rollback",
614
+ "Begin",
615
+ ]),
612
616
  gte(
613
617
  statementsTable.lastSeen,
614
618
  sql`DATE_SUB
@@ -619,12 +623,11 @@ export async function printQueriesWithPlan(
619
623
  ),
620
624
  ),
621
625
  ),
622
- `Timeout ${timeoutMs}ms in printQueriesWithPlan - transient timeouts are usually fine; repeated timeouts mean this diagnostic query is consistently slow and should be investigated`
623
- ,
624
- timeoutMs+200,
626
+ `Timeout ${timeoutMs}ms in printQueriesWithPlan - transient timeouts are usually fine; repeated timeouts mean this diagnostic query is consistently slow and should be investigated`,
627
+ timeoutMs + 200,
625
628
  );
626
629
 
627
- results.forEach((result) => {
630
+ for (const result of results) {
628
631
  // Average execution time (convert from nanoseconds to milliseconds)
629
632
  const avgTimeMs = Number(result.avgLatency) / 1_000_000;
630
633
  const avgMemMB = Number(result.avgMem) / 1_000_000;
@@ -634,7 +637,7 @@ export async function printQueriesWithPlan(
634
637
  console.warn(
635
638
  `SQL: ${result.digestText} | Memory: ${avgMemMB.toFixed(2)} MB | Time: ${avgTimeMs.toFixed(2)} ms | stmtType: ${result.stmtType} | Executions: ${result.execCount}\n Plan:${result.plan}`,
636
639
  );
637
- });
640
+ }
638
641
  } catch (error) {
639
642
  // eslint-disable-next-line no-console
640
643
  console.debug(
@@ -644,11 +647,11 @@ export async function printQueriesWithPlan(
644
647
  }
645
648
  }
646
649
 
647
- const SESSION_ALIAS_NAME_ORM = 'orm';
650
+ const SESSION_ALIAS_NAME_ORM = "orm";
648
651
 
649
652
  /**
650
653
  * Analyzes and logs slow queries from the last specified number of hours.
651
- *
654
+ *
652
655
  * This function queries the slow query system table to find queries that were executed
653
656
  * within the specified time window and logs detailed performance information including:
654
657
  * - SQL query text
@@ -656,77 +659,81 @@ const SESSION_ALIAS_NAME_ORM = 'orm';
656
659
  * - Query execution time (in ms)
657
660
  * - Execution count
658
661
  * - Execution plan
659
- *
662
+ *
660
663
  * @param forgeSQLORM - The ForgeSQL operation instance for database access
661
664
  * @param hours - Number of hours to look back for slow queries (e.g., 1 for last hour, 24 for last day)
662
665
  * @param timeout - Optional timeout in milliseconds for the query execution (defaults to 1500ms)
663
- *
666
+ *
664
667
  * @example
665
668
  * ```typescript
666
669
  * // Analyze slow queries from the last hour
667
670
  * await slowQueryPerHours(forgeSQLORM, 1);
668
- *
671
+ *
669
672
  * // Analyze slow queries from the last 24 hours with custom timeout
670
673
  * await slowQueryPerHours(forgeSQLORM, 24, 3000);
671
- *
674
+ *
672
675
  * // Analyze slow queries from the last 6 hours
673
676
  * await slowQueryPerHours(forgeSQLORM, 6);
674
677
  * ```
675
- *
678
+ *
676
679
  * @throws Does not throw - errors are logged to console.debug instead
677
680
  */
678
- export async function slowQueryPerHours(forgeSQLORM: ForgeSqlOperation, hours:number, timeout?: number) {
679
- try {
680
- const timeoutMs = timeout ?? 1500;
681
- const results = await withTimeout(
682
- forgeSQLORM
683
- .getDrizzleQueryBuilder()
684
- .select({
685
- query: withTidbHint(slowQuery.query),
686
- queryTime: slowQuery.queryTime,
687
- memMax: slowQuery.memMax,
688
- plan: slowQuery.plan,
689
- })
690
- .from(slowQuery)
691
- .where(
692
- and(
693
- isNotNull(slowQuery.digest),
694
- ne(slowQuery.sessionAlias, SESSION_ALIAS_NAME_ORM),
695
- gte(
696
- slowQuery.time,
697
- sql`DATE_SUB
681
+ export async function slowQueryPerHours(
682
+ forgeSQLORM: ForgeSqlOperation,
683
+ hours: number,
684
+ timeout?: number,
685
+ ) {
686
+ try {
687
+ const timeoutMs = timeout ?? 1500;
688
+ const results = await withTimeout(
689
+ forgeSQLORM
690
+ .getDrizzleQueryBuilder()
691
+ .select({
692
+ query: withTidbHint(slowQuery.query),
693
+ queryTime: slowQuery.queryTime,
694
+ memMax: slowQuery.memMax,
695
+ plan: slowQuery.plan,
696
+ })
697
+ .from(slowQuery)
698
+ .where(
699
+ and(
700
+ isNotNull(slowQuery.digest),
701
+ ne(slowQuery.sessionAlias, SESSION_ALIAS_NAME_ORM),
702
+ gte(
703
+ slowQuery.time,
704
+ sql`DATE_SUB
698
705
  (NOW(), INTERVAL
699
706
  ${hours}
700
707
  HOUR
701
708
  )`,
702
- ),
703
- ),
704
- ),
705
- `Timeout ${timeoutMs}ms in slowQueryPerHours - transient timeouts are usually fine; repeated timeouts mean this diagnostic query is consistently slow and should be investigated`,
706
- timeoutMs,
707
- );
708
- const response:string[] =[]
709
- results.forEach((result) => {
710
- // Convert memory from bytes to MB and handle null values
711
- const memMaxMB = result.memMax ? Number(result.memMax) / 1_000_000 : 0;
712
-
713
- const message = `Found SlowQuery SQL: ${result.query} | Memory: ${memMaxMB.toFixed(2)} MB | Time: ${result.queryTime} ms\n Plan:${result.plan}`;
714
- response.push(message);
715
- // 1. Query info: SQL, memory, time, executions
716
- // eslint-disable-next-line no-console
717
- console.warn(
718
- message,
719
- );
720
- });
721
- return response;
722
- } catch (error) {
723
- // eslint-disable-next-line no-console
724
- console.debug(
725
- `Error occurred while retrieving query execution plan: ${error instanceof Error ? error.message : "Unknown error"}. Try again after some time`,
726
- error,
727
- );
728
- return [`Error occurred while retrieving query execution plan: ${error instanceof Error ? error.message : "Unknown error"}`]
709
+ ),
710
+ ),
711
+ ),
712
+ `Timeout ${timeoutMs}ms in slowQueryPerHours - transient timeouts are usually fine; repeated timeouts mean this diagnostic query is consistently slow and should be investigated`,
713
+ timeoutMs,
714
+ );
715
+ const response: string[] = [];
716
+ for (const result of results) {
717
+ // Convert memory from bytes to MB and handle null values
718
+ const memMaxMB = result.memMax ? Number(result.memMax) / 1_000_000 : 0;
719
+
720
+ const message = `Found SlowQuery SQL: ${result.query} | Memory: ${memMaxMB.toFixed(2)} MB | Time: ${result.queryTime} ms\n Plan:${result.plan}`;
721
+ response.push(message);
722
+ // 1. Query info: SQL, memory, time, executions
723
+ // eslint-disable-next-line no-console
724
+ console.warn(message);
729
725
  }
726
+ return response;
727
+ } catch (error) {
728
+ // eslint-disable-next-line no-console
729
+ console.debug(
730
+ `Error occurred while retrieving query execution plan: ${error instanceof Error ? error.message : "Unknown error"}. Try again after some time`,
731
+ error,
732
+ );
733
+ return [
734
+ `Error occurred while retrieving query execution plan: ${error instanceof Error ? error.message : "Unknown error"}`,
735
+ ];
736
+ }
730
737
  }
731
738
 
732
739
  /**
@@ -737,29 +744,33 @@ export async function slowQueryPerHours(forgeSQLORM: ForgeSqlOperation, hours:nu
737
744
  * @returns Promise that resolves with the result or rejects on timeout
738
745
  * @throws {Error} When the operation times out
739
746
  */
740
- export async function withTimeout<T>(promise: Promise<T>, message: string, timeoutMs: number): Promise<T> {
741
- let timeoutId: ReturnType<typeof setTimeout> | undefined;
747
+ export async function withTimeout<T>(
748
+ promise: Promise<T>,
749
+ message: string,
750
+ timeoutMs: number,
751
+ ): Promise<T> {
752
+ let timeoutId: ReturnType<typeof setTimeout> | undefined;
753
+
754
+ const timeoutPromise = new Promise<never>((_, reject) => {
755
+ timeoutId = setTimeout(() => {
756
+ reject(new Error(message));
757
+ }, timeoutMs);
758
+ });
742
759
 
743
- const timeoutPromise = new Promise<never>((_, reject) => {
744
- timeoutId = setTimeout(() => {
745
- reject(
746
- new Error(message),
747
- );
748
- }, timeoutMs);
749
- });
750
-
751
- try {
752
- return await Promise.race([promise, timeoutPromise]);
753
- } finally {
754
- if (timeoutId) {
755
- clearTimeout(timeoutId);
756
- }
760
+ try {
761
+ return await Promise.race([promise, timeoutPromise]);
762
+ } finally {
763
+ if (timeoutId) {
764
+ clearTimeout(timeoutId);
757
765
  }
766
+ }
758
767
  }
759
768
 
760
- export function withTidbHint<TDataType extends ColumnDataType, TPartial extends Partial<ColumnBaseConfig<TDataType, string>>>(column: AnyMySqlColumn<TPartial>): AnyMySqlColumn<TPartial> {
761
- // We lie a bit to TypeScript here: at runtime this is a new SQL fragment,
762
- // but returning TExpr keeps the column type info in downstream inference.
763
- return sql`/*+ SET_VAR(tidb_session_alias=${sql.raw(`${SESSION_ALIAS_NAME_ORM}`)}) */ ${column}` as unknown as AnyMySqlColumn<TPartial>;
769
+ export function withTidbHint<
770
+ TDataType extends ColumnDataType,
771
+ TPartial extends Partial<ColumnBaseConfig<TDataType, string>>,
772
+ >(column: AnyMySqlColumn<TPartial>): AnyMySqlColumn<TPartial> {
773
+ // We lie a bit to TypeScript here: at runtime this is a new SQL fragment,
774
+ // but returning TExpr keeps the column type info in downstream inference.
775
+ return sql`/*+ SET_VAR(tidb_session_alias=${sql.raw(`${SESSION_ALIAS_NAME_ORM}`)}) */ ${column}` as unknown as AnyMySqlColumn<TPartial>;
764
776
  }
765
-
@@ -1,45 +1,45 @@
1
- import {getHttpResponse, TriggerResponse} from "./index";
2
- import {slowQueryPerHours} from "../utils/sqlUtils";
3
- import {ForgeSqlOperation} from "../core/ForgeSQLQueryBuilder";
1
+ import { getHttpResponse, TriggerResponse } from "./index";
2
+ import { slowQueryPerHours } from "../utils/sqlUtils";
3
+ import { ForgeSqlOperation } from "../core/ForgeSQLQueryBuilder";
4
4
 
5
5
  /**
6
6
  * Scheduler trigger for analyzing slow queries from the last specified number of hours.
7
- *
7
+ *
8
8
  * This trigger analyzes slow queries from TiDB's slow query log system table and provides
9
9
  * detailed performance information including SQL query text, memory usage, execution time,
10
10
  * and execution plans. It's designed to be used as a scheduled trigger in Atlassian Forge
11
11
  * to monitor query performance over time.
12
- *
12
+ *
13
13
  * The function queries the slow query system table to find queries executed within the
14
14
  * specified time window and logs detailed performance information to the console. Results
15
15
  * are limited to the top 50 slow queries to prevent excessive output.
16
- *
16
+ *
17
17
  * @param forgeSQLORM - The ForgeSQL operation instance for database access
18
18
  * @param options - Configuration options for the slow query analysis
19
19
  * @param options.hours - Number of hours to look back for slow queries (default: 1)
20
20
  * @param options.timeout - Timeout in milliseconds for the query execution (default: 2000ms)
21
- *
21
+ *
22
22
  * @returns Promise<TriggerResponse<string>> - HTTP response with JSON stringified query results or error message
23
- *
23
+ *
24
24
  * @example
25
25
  * ```typescript
26
26
  * import ForgeSQL, { slowQuerySchedulerTrigger } from "forge-sql-orm";
27
- *
27
+ *
28
28
  * const forgeSQL = new ForgeSQL();
29
- *
29
+ *
30
30
  * // Basic usage with default options (1 hour, 2000ms timeout)
31
31
  * export const slowQueryTrigger = () =>
32
32
  * slowQuerySchedulerTrigger(forgeSQL, { hours: 1, timeout: 2000 });
33
- *
33
+ *
34
34
  * // Analyze slow queries from the last 6 hours with extended timeout
35
35
  * export const sixHourSlowQueryTrigger = () =>
36
36
  * slowQuerySchedulerTrigger(forgeSQL, { hours: 6, timeout: 5000 });
37
- *
37
+ *
38
38
  * // Analyze slow queries from the last 24 hours
39
39
  * export const dailySlowQueryTrigger = () =>
40
40
  * slowQuerySchedulerTrigger(forgeSQL, { hours: 24, timeout: 3000 });
41
41
  * ```
42
- *
42
+ *
43
43
  * @example
44
44
  * ```yaml
45
45
  * # manifest.yml configuration
@@ -47,36 +47,43 @@ import {ForgeSqlOperation} from "../core/ForgeSQLQueryBuilder";
47
47
  * - key: slow-query-trigger
48
48
  * function: slowQueryTrigger
49
49
  * interval: hour
50
- *
50
+ *
51
51
  * function:
52
52
  * - key: slowQueryTrigger
53
53
  * handler: index.slowQueryTrigger
54
54
  * ```
55
- *
55
+ *
56
56
  * @remarks
57
57
  * - Results are automatically logged to the Forge Developer Console via `console.warn()`
58
58
  * - The function returns up to 50 slow queries to prevent excessive logging
59
59
  * - Transient timeouts are usually fine; repeated timeouts indicate the diagnostic query itself is slow
60
60
  * - This trigger is best used with hourly intervals to catch slow queries in a timely manner
61
61
  * - Error responses return HTTP 500 with error details
62
- *
62
+ *
63
63
  * @see {@link slowQueryPerHours} - The underlying function that performs the actual query analysis
64
64
  */
65
- export async function slowQuerySchedulerTrigger(forgeSQLORM: ForgeSqlOperation, options: {
66
- hours: number,
67
- timeout: number
68
- }): Promise<TriggerResponse<string>> {
69
- try {
70
- return getHttpResponse<string>(200, JSON.stringify(await slowQueryPerHours(forgeSQLORM, options?.hours ?? 1, options?.timeout ?? 3000)));
71
- } catch (error: any) {
72
- const errorMessage =
73
- error?.debug?.sqlMessage ??
74
- error?.debug?.message ??
75
- error.message ??
76
- "Unknown error occurred";
77
- // eslint-disable-next-line no-console
78
- console.error(errorMessage);
79
- return getHttpResponse<string>(500, errorMessage);
80
- }
65
+ export async function slowQuerySchedulerTrigger(
66
+ forgeSQLORM: ForgeSqlOperation,
67
+ options: {
68
+ hours: number;
69
+ timeout: number;
70
+ },
71
+ ): Promise<TriggerResponse<string>> {
72
+ try {
73
+ return getHttpResponse<string>(
74
+ 200,
75
+ JSON.stringify(
76
+ await slowQueryPerHours(forgeSQLORM, options?.hours ?? 1, options?.timeout ?? 3000),
77
+ ),
78
+ );
79
+ } catch (error: any) {
80
+ const errorMessage =
81
+ error?.debug?.sqlMessage ??
82
+ error?.debug?.message ??
83
+ error.message ??
84
+ "Unknown error occurred";
85
+ // eslint-disable-next-line no-console
86
+ console.error(errorMessage);
87
+ return getHttpResponse<string>(500, errorMessage);
88
+ }
81
89
  }
82
-