forge-sql-orm 2.1.14 → 2.1.16

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 (65) hide show
  1. package/README.md +294 -20
  2. package/dist/core/ForgeSQLORM.d.ts +16 -7
  3. package/dist/core/ForgeSQLORM.d.ts.map +1 -1
  4. package/dist/core/ForgeSQLORM.js +73 -15
  5. package/dist/core/ForgeSQLORM.js.map +1 -1
  6. package/dist/core/ForgeSQLQueryBuilder.d.ts +15 -7
  7. package/dist/core/ForgeSQLQueryBuilder.d.ts.map +1 -1
  8. package/dist/core/ForgeSQLQueryBuilder.js.map +1 -1
  9. package/dist/core/ForgeSQLSelectOperations.d.ts +2 -1
  10. package/dist/core/ForgeSQLSelectOperations.d.ts.map +1 -1
  11. package/dist/core/ForgeSQLSelectOperations.js.map +1 -1
  12. package/dist/core/Rovo.d.ts +40 -0
  13. package/dist/core/Rovo.d.ts.map +1 -1
  14. package/dist/core/Rovo.js +164 -138
  15. package/dist/core/Rovo.js.map +1 -1
  16. package/dist/index.d.ts +1 -2
  17. package/dist/index.d.ts.map +1 -1
  18. package/dist/index.js +3 -2
  19. package/dist/index.js.map +1 -1
  20. package/dist/lib/drizzle/extensions/additionalActions.d.ts.map +1 -1
  21. package/dist/lib/drizzle/extensions/additionalActions.js +72 -22
  22. package/dist/lib/drizzle/extensions/additionalActions.js.map +1 -1
  23. package/dist/utils/cacheTableUtils.d.ts +11 -0
  24. package/dist/utils/cacheTableUtils.d.ts.map +1 -0
  25. package/dist/utils/cacheTableUtils.js +450 -0
  26. package/dist/utils/cacheTableUtils.js.map +1 -0
  27. package/dist/utils/cacheUtils.d.ts.map +1 -1
  28. package/dist/utils/cacheUtils.js +3 -22
  29. package/dist/utils/cacheUtils.js.map +1 -1
  30. package/dist/utils/forgeDriver.d.ts +3 -2
  31. package/dist/utils/forgeDriver.d.ts.map +1 -1
  32. package/dist/utils/forgeDriver.js +24 -27
  33. package/dist/utils/forgeDriver.js.map +1 -1
  34. package/dist/utils/metadataContextUtils.d.ts +27 -1
  35. package/dist/utils/metadataContextUtils.d.ts.map +1 -1
  36. package/dist/utils/metadataContextUtils.js +237 -10
  37. package/dist/utils/metadataContextUtils.js.map +1 -1
  38. package/dist/utils/sqlUtils.d.ts +1 -0
  39. package/dist/utils/sqlUtils.d.ts.map +1 -1
  40. package/dist/utils/sqlUtils.js +217 -119
  41. package/dist/utils/sqlUtils.js.map +1 -1
  42. package/dist/webtriggers/applyMigrationsWebTrigger.js +1 -1
  43. package/dist/webtriggers/index.d.ts +1 -0
  44. package/dist/webtriggers/index.d.ts.map +1 -1
  45. package/dist/webtriggers/index.js +1 -0
  46. package/dist/webtriggers/index.js.map +1 -1
  47. package/dist/webtriggers/topSlowestStatementLastHourTrigger.d.ts +60 -0
  48. package/dist/webtriggers/topSlowestStatementLastHourTrigger.d.ts.map +1 -0
  49. package/dist/webtriggers/topSlowestStatementLastHourTrigger.js +55 -0
  50. package/dist/webtriggers/topSlowestStatementLastHourTrigger.js.map +1 -0
  51. package/package.json +11 -10
  52. package/src/core/ForgeSQLORM.ts +78 -14
  53. package/src/core/ForgeSQLQueryBuilder.ts +15 -5
  54. package/src/core/ForgeSQLSelectOperations.ts +2 -1
  55. package/src/core/Rovo.ts +209 -167
  56. package/src/index.ts +1 -3
  57. package/src/lib/drizzle/extensions/additionalActions.ts +98 -42
  58. package/src/utils/cacheTableUtils.ts +511 -0
  59. package/src/utils/cacheUtils.ts +3 -25
  60. package/src/utils/forgeDriver.ts +38 -29
  61. package/src/utils/metadataContextUtils.ts +290 -10
  62. package/src/utils/sqlUtils.ts +298 -142
  63. package/src/webtriggers/applyMigrationsWebTrigger.ts +1 -1
  64. package/src/webtriggers/index.ts +1 -0
  65. package/src/webtriggers/topSlowestStatementLastHourTrigger.ts +69 -0
package/README.md CHANGED
@@ -10,6 +10,11 @@
10
10
  [![forge-sql-orm CI](https://github.com/vzakharchenko/forge-sql-orm/actions/workflows/node.js.yml/badge.svg)](https://github.com/vzakharchenko/forge-sql-orm/actions/workflows/node.js.yml)
11
11
  [![Coverage Status](https://coveralls.io/repos/github/vzakharchenko/forge-sql-orm/badge.svg?branch=master)](https://coveralls.io/github/vzakharchenko/forge-sql-orm?branch=master)
12
12
  [![DeepScan grade](https://deepscan.io/api/teams/26652/projects/29272/branches/940614/badge/grade.svg)](https://deepscan.io/dashboard#view=project&tid=26652&pid=29272&bid=940614)
13
+ [![Snyk Vulnerabilities](https://snyk.io/test/github/vzakharchenko/forge-sql-orm/badge.svg)](https://snyk.io/test/github/vzakharchenko/forge-sql-orm)
14
+
15
+ [![Quality Gate Status](https://sonarcloud.io/api/project_badges/measure?project=vzakharchenko_forge-sql-orm&metric=alert_status)](https://sonarcloud.io/summary/new_code?id=vzakharchenko_forge-sql-orm)
16
+ [![Bugs](https://sonarcloud.io/api/project_badges/measure?project=vzakharchenko_forge-sql-orm&metric=bugs)](https://sonarcloud.io/summary/new_code?id=vzakharchenko_forge-sql-orm)
17
+ [![Code Smells](https://sonarcloud.io/api/project_badges/measure?project=vzakharchenko_forge-sql-orm&metric=code_smells)](https://sonarcloud.io/summary/new_code?id=vzakharchenko_forge-sql-orm)
13
18
 
14
19
  **Forge-SQL-ORM** is an ORM designed for working with [@forge/sql](https://developer.atlassian.com/platform/forge/storage-reference/sql-tutorial/) in **Atlassian Forge**. It is built on top of [Drizzle ORM](https://orm.drizzle.team) and provides advanced capabilities for working with relational databases inside Forge.
15
20
 
@@ -22,7 +27,7 @@
22
27
  - ✅ **Type-Safe Query Building**: Write SQL queries with full TypeScript support
23
28
  - ✅ **Supports complex SQL queries** with joins and filtering using Drizzle ORM
24
29
  - ✅ **Advanced Query Methods**: `selectFrom()`, `selectDistinctFrom()`, `selectCacheableFrom()`, `selectDistinctCacheableFrom()` for all-column queries with field aliasing
25
- - ✅ **Query Execution with Metadata**: `executeWithMetadata()` method for capturing detailed execution metrics including database execution time, response size, and query analysis capabilities with performance monitoring
30
+ - ✅ **Query Execution with Metadata**: `executeWithMetadata()` method for capturing detailed execution metrics including database execution time, response size, and query analysis capabilities with performance monitoring. Supports two modes for query plan printing: TopSlowest mode (default) and SummaryTable mode
26
31
  - ✅ **Raw SQL Execution**: `execute()`, `executeCacheable()`, `executeDDL()`, and `executeDDLActions()` methods for direct SQL queries with local and global caching
27
32
  - ✅ **Common Table Expressions (CTEs)**: `with()` method for complex queries with subqueries
28
33
  - ✅ **Schema migration support**, allowing automatic schema evolution
@@ -347,6 +352,11 @@ resolver.define("fetch", async (req: Request) => {
347
352
  console.debug(`[Performance Debug fetch] High DB time: ${totalDbExecutionTime} ms`);
348
353
  }
349
354
  },
355
+ {
356
+ // Optional: Configure query plan printing behavior
357
+ mode: "TopSlowest", // Print top slowest queries (default)
358
+ topQueries: 3, // Print top 3 slowest queries
359
+ },
350
360
  );
351
361
  } catch (e) {
352
362
  const error = e?.cause?.debug?.sqlMessage ?? e?.cause;
@@ -356,6 +366,19 @@ resolver.define("fetch", async (req: Request) => {
356
366
  });
357
367
  ```
358
368
 
369
+ **Query Plan Printing Options:**
370
+
371
+ The `printQueriesWithPlan` function supports two modes:
372
+
373
+ 1. **TopSlowest Mode (default)**: Prints execution plans for the slowest queries from the current resolver invocation
374
+ - `mode`: Set to `'TopSlowest'` (default)
375
+ - `topQueries`: Number of top slowest queries to analyze (default: 1)
376
+
377
+ 2. **SummaryTable Mode**: Uses `CLUSTER_STATEMENTS_SUMMARY` for query analysis
378
+ - `mode`: Set to `'SummaryTable'`
379
+ - `summaryTableWindowTime`: Time window in milliseconds (default: 15000ms)
380
+ - Only works if queries are executed within the specified time window
381
+
359
382
  ### 5. Rovo Integration (Secure Analytics)
360
383
 
361
384
  ```typescript
@@ -452,6 +475,11 @@ const usersWithMetadata = await forgeSQL.executeWithMetadata(
452
475
 
453
476
  console.log(`DB response size: ${totalResponseSize} bytes`);
454
477
  },
478
+ {
479
+ // Optional: Configure query plan printing
480
+ mode: "TopSlowest", // Print top slowest queries (default)
481
+ topQueries: 2, // Print top 2 slowest queries
482
+ },
455
483
  );
456
484
 
457
485
  // DDL operations for schema modifications
@@ -589,7 +617,12 @@ const usersWithMetadata = await forgeSQL.executeWithMetadata(
589
617
  }
590
618
 
591
619
  console.log(`DB response size: ${totalResponseSize} bytes`);
592
- }
620
+ },
621
+ {
622
+ // Optional: Configure query plan printing
623
+ mode: 'TopSlowest', // Print top slowest queries (default)
624
+ topQueries: 1, // Print top slowest query
625
+ },
593
626
  );
594
627
  ```
595
628
 
@@ -993,23 +1026,23 @@ const optimizedData = await forgeSQL.executeWithLocalCacheContextAndReturnValue(
993
1026
 
994
1027
  ### When to Use Each Approach
995
1028
 
996
- | Method | Use Case | Versioning | Cache Management |
997
- | ---------------------------------------------------------------------- | ----------------------------------------------------------- | ---------- | -------------------- |
998
- | `insertWithCacheContext/insertWithCacheContext/updateWithCacheContext` | Basic Drizzle operations | ❌ No | Cache Context |
999
- | `insertAndEvictCache()` | Simple inserts without conflicts | ❌ No | ✅ Yes |
1000
- | `updateAndEvictCache()` | Simple updates without conflicts | ❌ No | ✅ Yes |
1001
- | `deleteAndEvictCache()` | Simple deletes without conflicts | ❌ No | ✅ Yes |
1002
- | `insert/update/delete` | Basic Drizzle operations | ❌ No | ❌ No |
1003
- | `selectFrom()` | All-column queries with field aliasing | ❌ No | Local Cache |
1004
- | `selectDistinctFrom()` | Distinct all-column queries with field aliasing | ❌ No | Local Cache |
1005
- | `selectCacheableFrom()` | All-column queries with field aliasing and caching | ❌ No | Local + Global Cache |
1006
- | `selectDistinctCacheableFrom()` | Distinct all-column queries with field aliasing and caching | ❌ No | Local + Global Cache |
1007
- | `execute()` | Raw SQL queries with local caching | ❌ No | Local Cache |
1008
- | `executeCacheable()` | Raw SQL queries with local and global caching | ❌ No | Local + Global Cache |
1009
- | `executeWithMetadata()` | Raw SQL queries with execution metrics capture | ❌ No | Local Cache |
1010
- | `executeDDL()` | DDL operations (CREATE, ALTER, DROP, etc.) | ❌ No | No Caching |
1011
- | `executeDDLActions()` | Execute regular SQL queries in DDL operation context | ❌ No | No Caching |
1012
- | `with()` | Common Table Expressions (CTEs) | ❌ No | Local Cache |
1029
+ | Method | Use Case | Versioning | Cache Management |
1030
+ | ---------------------------------------------------------------------- | ---------------------------------------------------------------------------------------------------------------------- | ---------- | -------------------- |
1031
+ | `insertWithCacheContext/insertWithCacheContext/updateWithCacheContext` | Basic Drizzle operations | ❌ No | Cache Context |
1032
+ | `insertAndEvictCache()` | Simple inserts without conflicts | ❌ No | ✅ Yes |
1033
+ | `updateAndEvictCache()` | Simple updates without conflicts | ❌ No | ✅ Yes |
1034
+ | `deleteAndEvictCache()` | Simple deletes without conflicts | ❌ No | ✅ Yes |
1035
+ | `insert/update/delete` | Basic Drizzle operations | ❌ No | ❌ No |
1036
+ | `selectFrom()` | All-column queries with field aliasing | ❌ No | Local Cache |
1037
+ | `selectDistinctFrom()` | Distinct all-column queries with field aliasing | ❌ No | Local Cache |
1038
+ | `selectCacheableFrom()` | All-column queries with field aliasing and caching | ❌ No | Local + Global Cache |
1039
+ | `selectDistinctCacheableFrom()` | Distinct all-column queries with field aliasing and caching | ❌ No | Local + Global Cache |
1040
+ | `execute()` | Raw SQL queries with local caching | ❌ No | Local Cache |
1041
+ | `executeCacheable()` | Raw SQL queries with local and global caching | ❌ No | Local + Global Cache |
1042
+ | `executeWithMetadata()` | Resolver-level profiling with execution metrics and configurable query plan printing (TopSlowest or SummaryTable mode) | ❌ No | Local Cache |
1043
+ | `executeDDL()` | DDL operations (CREATE, ALTER, DROP, etc.) | ❌ No | No Caching |
1044
+ | `executeDDLActions()` | Execute regular SQL queries in DDL operation context | ❌ No | No Caching |
1045
+ | `with()` | Common Table Expressions (CTEs) | ❌ No | Local Cache |
1013
1046
 
1014
1047
  where Cache context - allows you to batch cache invalidation events and bypass cache reads for affected tables.
1015
1048
 
@@ -1413,7 +1446,12 @@ const usersWithMetadata = await forgeSQL.executeWithMetadata(
1413
1446
  }
1414
1447
 
1415
1448
  console.log(`DB response size: ${totalResponseSize} bytes`);
1416
- }
1449
+ },
1450
+ {
1451
+ // Optional: Configure query plan printing
1452
+ mode: 'TopSlowest', // Print top slowest queries (default)
1453
+ topQueries: 1, // Print top slowest query
1454
+ },
1417
1455
  );
1418
1456
 
1419
1457
  // Using executeDDL() for DDL operations (CREATE, ALTER, DROP, etc.)
@@ -1781,6 +1819,11 @@ await forgeSQL.executeWithLocalContext(async () => {
1781
1819
 
1782
1820
  console.log(`DB response size: ${totalResponseSize} bytes`);
1783
1821
  },
1822
+ {
1823
+ // Optional: Configure query plan printing
1824
+ topQueries: 1, // Print top slowest query (default)
1825
+ mode: "TopSlowest", // Print top slowest queries (default)
1826
+ },
1784
1827
  );
1785
1828
 
1786
1829
  // Insert operation - evicts local cache for users table
@@ -1982,6 +2025,11 @@ const usersWithMetadata = await forgeSQL.executeWithMetadata(
1982
2025
 
1983
2026
  console.log(`DB response size: ${totalResponseSize} bytes`);
1984
2027
  },
2028
+ {
2029
+ // Optional: Configure query plan printing
2030
+ mode: "TopSlowest", // Print top slowest queries (default)
2031
+ topQueries: 1, // Print top slowest query
2032
+ },
1985
2033
  );
1986
2034
  ```
1987
2035
 
@@ -2622,6 +2670,227 @@ The error analysis mechanism:
2622
2670
 
2623
2671
  > **💡 Tip**: The automatic error analysis only triggers for timeout and OOM errors. Other errors are logged normally without plan analysis.
2624
2672
 
2673
+ ### Resolver-Level Performance Monitoring
2674
+
2675
+ The `executeWithMetadata()` method provides resolver-level profiling with configurable query plan printing. It aggregates metrics across all database operations within a resolver and supports two modes for query plan analysis.
2676
+
2677
+ #### Basic Usage
2678
+
2679
+ ```typescript
2680
+ const result = await forgeSQL.executeWithMetadata(
2681
+ async () => {
2682
+ const users = await forgeSQL.selectFrom(usersTable);
2683
+ const orders = await forgeSQL
2684
+ .selectFrom(ordersTable)
2685
+ .where(eq(ordersTable.userId, usersTable.id));
2686
+ return { users, orders };
2687
+ },
2688
+ async (totalDbExecutionTime, totalResponseSize, printQueriesWithPlan) => {
2689
+ const threshold = 500; // ms baseline for this resolver
2690
+
2691
+ if (totalDbExecutionTime > threshold * 1.5) {
2692
+ console.warn(`[Performance Warning] Resolver exceeded DB time: ${totalDbExecutionTime} ms`);
2693
+ await printQueriesWithPlan(); // Analyze and print query execution plans
2694
+ } else if (totalDbExecutionTime > threshold) {
2695
+ console.debug(`[Performance Debug] High DB time: ${totalDbExecutionTime} ms`);
2696
+ }
2697
+
2698
+ console.log(`DB response size: ${totalResponseSize} bytes`);
2699
+ },
2700
+ );
2701
+ ```
2702
+
2703
+ #### Query Plan Printing Options
2704
+
2705
+ The `printQueriesWithPlan` function supports two modes, configurable via the optional `options` parameter:
2706
+
2707
+ **1. TopSlowest Mode (default)**: Prints execution plans for the slowest queries from the current resolver invocation
2708
+
2709
+ ```typescript
2710
+ // Full configuration example
2711
+ const result = await forgeSQL.executeWithMetadata(
2712
+ async () => {
2713
+ const users = await forgeSQL.selectFrom(usersTable);
2714
+ return users;
2715
+ },
2716
+ async (totalDbExecutionTime, totalResponseSize, printQueriesWithPlan) => {
2717
+ if (totalDbExecutionTime > 1000) {
2718
+ await printQueriesWithPlan(); // Will print top 3 slowest queries with execution plans
2719
+ }
2720
+ },
2721
+ {
2722
+ mode: "TopSlowest", // Print top slowest queries (default)
2723
+ topQueries: 3, // Number of top slowest queries to analyze (default: 1)
2724
+ showSlowestPlans: true, // Show execution plans (default: true)
2725
+ },
2726
+ );
2727
+
2728
+ // Minimal configuration - only specify what you need
2729
+ const result2 = await forgeSQL.executeWithMetadata(
2730
+ async () => {
2731
+ const users = await forgeSQL.selectFrom(usersTable);
2732
+ return users;
2733
+ },
2734
+ async (totalDbExecutionTime, totalResponseSize, printQueriesWithPlan) => {
2735
+ if (totalDbExecutionTime > 1000) {
2736
+ await printQueriesWithPlan(); // Will print top 3 slowest queries (all other options use defaults)
2737
+ }
2738
+ },
2739
+ {
2740
+ topQueries: 3, // Only specify topQueries, mode and showSlowestPlans use defaults
2741
+ },
2742
+ );
2743
+
2744
+ // Disable execution plans - only show SQL and execution time
2745
+ const result3 = await forgeSQL.executeWithMetadata(
2746
+ async () => {
2747
+ const users = await forgeSQL.selectFrom(usersTable);
2748
+ return users;
2749
+ },
2750
+ async (totalDbExecutionTime, totalResponseSize, printQueriesWithPlan) => {
2751
+ if (totalDbExecutionTime > 1000) {
2752
+ await printQueriesWithPlan(); // Will print SQL and time only, no execution plans
2753
+ }
2754
+ },
2755
+ {
2756
+ showSlowestPlans: false, // Disable execution plan printing
2757
+ },
2758
+ );
2759
+
2760
+ // Use all defaults - pass empty object or omit options parameter
2761
+ const result4 = await forgeSQL.executeWithMetadata(
2762
+ async () => {
2763
+ const users = await forgeSQL.selectFrom(usersTable);
2764
+ return users;
2765
+ },
2766
+ async (totalDbExecutionTime, totalResponseSize, printQueriesWithPlan) => {
2767
+ if (totalDbExecutionTime > 1000) {
2768
+ await printQueriesWithPlan(); // Uses all defaults: TopSlowest mode, topQueries: 1, showSlowestPlans: true
2769
+ }
2770
+ },
2771
+ {}, // Empty object - all options use defaults
2772
+ );
2773
+ ```
2774
+
2775
+ <|tool▁calls▁begin|><|tool▁call▁begin|>
2776
+ read_file
2777
+
2778
+ **2. SummaryTable Mode**: Uses `CLUSTER_STATEMENTS_SUMMARY` for query analysis
2779
+
2780
+ ```typescript
2781
+ const result = await forgeSQL.executeWithMetadata(
2782
+ async () => {
2783
+ const users = await forgeSQL.selectFrom(usersTable);
2784
+ return users;
2785
+ },
2786
+ async (totalDbExecutionTime, totalResponseSize, printQueriesWithPlan) => {
2787
+ if (totalDbExecutionTime > 1000) {
2788
+ await printQueriesWithPlan(); // Will use CLUSTER_STATEMENTS_SUMMARY if within time window
2789
+ }
2790
+ },
2791
+ {
2792
+ mode: "SummaryTable", // Use SummaryTable mode
2793
+ summaryTableWindowTime: 10000, // Time window in milliseconds (default: 15000ms)
2794
+ },
2795
+ );
2796
+ ```
2797
+
2798
+ #### Configuration Options
2799
+
2800
+ All options are **optional**. If not specified, default values are used. You can pass only the options you need to customize.
2801
+
2802
+ | Option | Type | Default | Description |
2803
+ | ------------------------ | -------------------------------- | -------------- | --------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
2804
+ | `mode` | `'TopSlowest' \| 'SummaryTable'` | `'TopSlowest'` | Query plan printing mode. `'TopSlowest'` prints execution plans for the slowest queries from the current resolver. `'SummaryTable'` uses `CLUSTER_STATEMENTS_SUMMARY` when within time window |
2805
+ | `summaryTableWindowTime` | `number` | `15000` | Time window in milliseconds for summary table queries. Only used when `mode` is `'SummaryTable'` |
2806
+ | `topQueries` | `number` | `1` | Number of top slowest queries to analyze when `mode` is `'TopSlowest'` |
2807
+ | `showSlowestPlans` | `boolean` | `true` | Whether to show execution plans for slowest queries in TopSlowest mode. If `false`, only SQL and execution time are printed |
2808
+ | `normalizeQuery` | `boolean` | `true` | Whether to normalize SQL queries by replacing parameter values with `?` placeholders. Set to `false` to disable normalization if it causes issues with complex queries |
2809
+
2810
+ **Examples:**
2811
+
2812
+ ```typescript
2813
+ // Use all defaults - omit options or pass empty object
2814
+ await forgeSQL.executeWithMetadata(queryFn, onMetadataFn); // or { }
2815
+
2816
+ // Customize only what you need
2817
+ await forgeSQL.executeWithMetadata(queryFn, onMetadataFn, { topQueries: 3 });
2818
+ await forgeSQL.executeWithMetadata(queryFn, onMetadataFn, { mode: "SummaryTable" });
2819
+ await forgeSQL.executeWithMetadata(queryFn, onMetadataFn, { showSlowestPlans: false });
2820
+ await forgeSQL.executeWithMetadata(queryFn, onMetadataFn, { normalizeQuery: false }); // Disable query normalization
2821
+
2822
+ // Combine multiple options
2823
+ await forgeSQL.executeWithMetadata(queryFn, onMetadataFn, {
2824
+ mode: "TopSlowest",
2825
+ topQueries: 5,
2826
+ showSlowestPlans: false,
2827
+ normalizeQuery: true, // Enable query normalization (default)
2828
+ });
2829
+ ```
2830
+
2831
+ #### How It Works
2832
+
2833
+ 1. **TopSlowest Mode** (default):
2834
+ - Collects all queries executed within the resolver
2835
+ - Sorts them by execution time (slowest first)
2836
+ - Prints execution plans for the top N queries (configurable via `topQueries`)
2837
+ - If `showSlowestPlans` is `false`, only prints SQL and execution time without plans
2838
+ - Works immediately after query execution
2839
+
2840
+ 2. **SummaryTable Mode**:
2841
+ - Attempts to use `CLUSTER_STATEMENTS_SUMMARY` for query analysis
2842
+ - Only works if queries are executed within the specified time window (`summaryTableWindowTime`)
2843
+ - If the time window expires, falls back to TopSlowest mode
2844
+ - Provides aggregated statistics from TiDB's system tables
2845
+
2846
+ #### Example: Real-World Resolver
2847
+
2848
+ ```typescript
2849
+ resolver.define("fetch", async (req: Request) => {
2850
+ try {
2851
+ return await forgeSQL.executeWithMetadata(
2852
+ async () => {
2853
+ const users = await forgeSQL.selectFrom(demoUsers);
2854
+ const orders = await forgeSQL
2855
+ .selectFrom(demoOrders)
2856
+ .where(eq(demoOrders.userId, demoUsers.id));
2857
+ return { users, orders };
2858
+ },
2859
+ async (totalDbExecutionTime, totalResponseSize, printQueriesWithPlan) => {
2860
+ const threshold = 500; // ms baseline for this resolver
2861
+
2862
+ if (totalDbExecutionTime > threshold * 1.5) {
2863
+ console.warn(
2864
+ `[Performance Warning fetch] Resolver exceeded DB time: ${totalDbExecutionTime} ms`,
2865
+ );
2866
+ await printQueriesWithPlan(); // Analyze and print query execution plans
2867
+ } else if (totalDbExecutionTime > threshold) {
2868
+ console.debug(`[Performance Debug] High DB time: ${totalDbExecutionTime} ms`);
2869
+ }
2870
+ },
2871
+ {
2872
+ mode: "TopSlowest", // Print top slowest queries (default)
2873
+ topQueries: 2, // Print top 2 slowest queries
2874
+ },
2875
+ );
2876
+ } catch (e) {
2877
+ const error = e?.cause?.debug?.sqlMessage ?? e?.cause;
2878
+ console.error(error, e);
2879
+ throw error;
2880
+ }
2881
+ });
2882
+ ```
2883
+
2884
+ #### Benefits
2885
+
2886
+ - **Resolver-Level Profiling**: Aggregates metrics across all database operations in a resolver
2887
+ - **Configurable Analysis**: Choose between TopSlowest mode or SummaryTable mode
2888
+ - **Automatic Plan Formatting**: Execution plans are formatted in a readable format
2889
+ - **Performance Thresholds**: Set custom thresholds for performance warnings
2890
+ - **Zero Configuration**: Works out of the box with sensible defaults
2891
+
2892
+ > **💡 Tip**: When multiple resolvers are running concurrently, their query data may also appear in `printQueriesWithPlan()` analysis when using SummaryTable mode, as it queries the global `CLUSTER_STATEMENTS_SUMMARY` table.
2893
+
2625
2894
  ### Slow Query Monitoring
2626
2895
 
2627
2896
  Forge-SQL-ORM provides a scheduler trigger (`slowQuerySchedulerTrigger`) that automatically monitors and analyzes slow queries on an hourly basis. This trigger queries TiDB's slow query log system table and provides detailed performance information including SQL query text, memory usage, execution time, and execution plans.
@@ -2912,6 +3181,11 @@ const usersWithMetadata = await forgeSQL.executeWithMetadata(
2912
3181
 
2913
3182
  console.log(`DB response size: ${totalResponseSize} bytes`);
2914
3183
  },
3184
+ {
3185
+ // Optional: Configure query plan printing
3186
+ mode: "TopSlowest", // Print top slowest queries (default)
3187
+ topQueries: 1, // Print top slowest query
3188
+ },
2915
3189
  );
2916
3190
 
2917
3191
  // ✅ DDL operations for schema modifications
@@ -8,6 +8,7 @@ import { MySqlTable } from "drizzle-orm/mysql-core/table";
8
8
  import { MySqlDeleteBase, MySqlInsertBuilder, MySqlUpdateBuilder } from "drizzle-orm/mysql-core/query-builders";
9
9
  import { SQLWrapper } from "drizzle-orm/sql/sql";
10
10
  import { WithSubquery } from "drizzle-orm/subquery";
11
+ import { MetadataQueryOptions } from "../utils/metadataContextUtils";
11
12
  import type { MySqlQueryResultKind } from "drizzle-orm/mysql-core/session";
12
13
  /**
13
14
  * Public class that acts as a wrapper around the private ForgeSQLORMImpl.
@@ -29,7 +30,15 @@ declare class ForgeSQLORM implements ForgeSqlOperation {
29
30
  * @param onMetadata - Callback function that receives aggregated execution metadata
30
31
  * @param onMetadata.totalDbExecutionTime - Total database execution time across all operations in the query function (in milliseconds)
31
32
  * @param onMetadata.totalResponseSize - Total response size across all operations (in bytes)
32
- * @param onMetadata.printQueries - Function to analyze and print query execution plans from CLUSTER_STATEMENTS_SUMMARY
33
+ * @param onMetadata.printQueriesWithPlan - Function to analyze and print query execution plans. Supports two modes:
34
+ * - TopSlowest: Prints execution plans for the slowest queries from the current resolver (default)
35
+ * - SummaryTable: Uses CLUSTER_STATEMENTS_SUMMARY if within time window
36
+ * @param options - Optional configuration for query plan printing behavior
37
+ * @param options.mode - Query plan printing mode: 'TopSlowest' (default) or 'SummaryTable'
38
+ * @param options.summaryTableWindowTime - Time window in milliseconds for summary table queries (default: 15000ms). Only used when mode is 'SummaryTable'
39
+ * @param options.topQueries - Number of top slowest queries to analyze when mode is 'TopSlowest' (default: 1)
40
+ * @param options.showSlowestPlans - Whether to show execution plans for slowest queries in TopSlowest mode (default: true)
41
+ * @param options.normalizeQuery - Whether to normalize SQL queries by replacing parameter values with '?' placeholders (default: true). Set to false to disable normalization if it causes issues
33
42
  * @returns Promise with the query result
34
43
  *
35
44
  * @example
@@ -41,12 +50,12 @@ declare class ForgeSQLORM implements ForgeSqlOperation {
41
50
  * const orders = await forgeSQL.selectFrom(ordersTable).where(eq(ordersTable.userId, usersTable.id));
42
51
  * return { users, orders };
43
52
  * },
44
- * (totalDbExecutionTime, totalResponseSize, printQueries) => {
53
+ * (totalDbExecutionTime, totalResponseSize, printQueriesWithPlan) => {
45
54
  * const threshold = 500; // ms baseline for this resolver
46
55
  *
47
56
  * if (totalDbExecutionTime > threshold * 1.5) {
48
57
  * console.warn(`[Performance Warning] Resolver exceeded DB time: ${totalDbExecutionTime} ms`);
49
- * await printQueries(); // Analyze and print query execution plans
58
+ * await printQueriesWithPlan(); // Analyze and print query execution plans
50
59
  * } else if (totalDbExecutionTime > threshold) {
51
60
  * console.debug(`[Performance Debug] High DB time: ${totalDbExecutionTime} ms`);
52
61
  * }
@@ -69,12 +78,12 @@ declare class ForgeSQLORM implements ForgeSqlOperation {
69
78
  * .where(eq(demoOrders.userId, demoUsers.id));
70
79
  * return { users, orders };
71
80
  * },
72
- * async (totalDbExecutionTime, totalResponseSize, printQueries) => {
81
+ * async (totalDbExecutionTime, totalResponseSize, printQueriesWithPlan) => {
73
82
  * const threshold = 500; // ms baseline for this resolver
74
83
  *
75
84
  * if (totalDbExecutionTime > threshold * 1.5) {
76
85
  * console.warn(`[Performance Warning fetch] Resolver exceeded DB time: ${totalDbExecutionTime} ms`);
77
- * await printQueries(); // Optionally log or capture diagnostics for further analysis
86
+ * await printQueriesWithPlan(); // Optionally log or capture diagnostics for further analysis
78
87
  * } else if (totalDbExecutionTime > threshold) {
79
88
  * console.debug(`[Performance Debug] High DB time: ${totalDbExecutionTime} ms`);
80
89
  * }
@@ -90,9 +99,9 @@ declare class ForgeSQLORM implements ForgeSqlOperation {
90
99
  * });
91
100
  * ```
92
101
  *
93
- * @note **Important**: When multiple resolvers are running concurrently, their query data may also appear in `printQueries()` analysis, as it queries the global `CLUSTER_STATEMENTS_SUMMARY` table.
102
+ * @note **Important**: When multiple resolvers are running concurrently, their query data may also appear in `printQueriesWithPlan()` analysis, as it queries the global `CLUSTER_STATEMENTS_SUMMARY` table.
94
103
  */
95
- executeWithMetadata<T>(query: () => Promise<T>, onMetadata: (totalDbExecutionTime: number, totalResponseSize: number, printQueriesWithPlan: () => Promise<void>) => Promise<void> | void): Promise<T>;
104
+ executeWithMetadata<T>(query: () => Promise<T>, onMetadata: (totalDbExecutionTime: number, totalResponseSize: number, printQueriesWithPlan: () => Promise<void>) => Promise<void> | void, options?: MetadataQueryOptions): Promise<T>;
96
105
  selectCacheable<TSelection extends SelectedFields>(fields: TSelection, cacheTTL?: number): MySqlSelectBuilder<TSelection, MySqlRemotePreparedQueryHKT>;
97
106
  selectDistinctCacheable<TSelection extends SelectedFields>(fields: TSelection, cacheTTL?: number): MySqlSelectBuilder<TSelection, MySqlRemotePreparedQueryHKT>;
98
107
  /**
@@ -1 +1 @@
1
- {"version":3,"file":"ForgeSQLORM.d.ts","sourceRoot":"","sources":["../../src/core/ForgeSQLORM.ts"],"names":[],"mappings":"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"}
1
+ {"version":3,"file":"ForgeSQLORM.d.ts","sourceRoot":"","sources":["../../src/core/ForgeSQLORM.ts"],"names":[],"mappings":"AACA,OAAO,EACL,6BAA6B,EAC7B,iBAAiB,EACjB,kBAAkB,EAClB,qBAAqB,EACrB,iBAAiB,EACjB,eAAe,EAChB,MAAM,wBAAwB,CAAC;AAEhC,OAAO,EAEL,mBAAmB,EACnB,2BAA2B,EAC3B,yBAAyB,EAC1B,MAAM,yBAAyB,CAAC;AAEjC,OAAO,KAAK,EAAE,cAAc,EAAE,MAAM,oDAAoD,CAAC;AACzF,OAAO,EAAE,kBAAkB,EAAE,MAAM,wBAAwB,CAAC;AAC5D,OAAO,EACL,uBAAuB,EACvB,uBAAuB,EAEvB,0BAA0B,EAC1B,kCAAkC,EAClC,yBAAyB,EACzB,iBAAiB,EACjB,uBAAuB,EACxB,MAAM,6CAA6C,CAAC;AAErD,OAAO,EAAE,uBAAuB,EAAE,MAAM,2BAA2B,CAAC;AACpE,OAAO,EAAE,UAAU,EAAE,MAAM,8BAA8B,CAAC;AAC1D,OAAO,EACL,eAAe,EACf,kBAAkB,EAClB,kBAAkB,EACnB,MAAM,uCAAuC,CAAC;AAG/C,OAAO,EAAE,UAAU,EAAE,MAAM,qBAAqB,CAAC;AACjD,OAAO,EAAE,YAAY,EAAE,MAAM,sBAAsB,CAAC;AACpD,OAAO,EAGL,oBAAoB,EACrB,MAAM,+BAA+B,CAAC;AAEvC,OAAO,KAAK,EAAE,oBAAoB,EAAE,MAAM,gCAAgC,CAAC;AAw2B3E;;;GAGG;AACH,cAAM,WAAY,YAAW,iBAAiB;IAC5C,OAAO,CAAC,QAAQ,CAAC,WAAW,CAAoB;gBAEpC,OAAO,CAAC,EAAE,kBAAkB;IAIxC;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;OAmFG;IACG,mBAAmB,CAAC,CAAC,EACzB,KAAK,EAAE,MAAM,OAAO,CAAC,CAAC,CAAC,EACvB,UAAU,EAAE,CACV,oBAAoB,EAAE,MAAM,EAC5B,iBAAiB,EAAE,MAAM,EACzB,oBAAoB,EAAE,MAAM,OAAO,CAAC,IAAI,CAAC,KACtC,OAAO,CAAC,IAAI,CAAC,GAAG,IAAI,EACzB,OAAO,CAAC,EAAE,oBAAoB,GAC7B,OAAO,CAAC,CAAC,CAAC;IAIb,eAAe,CAAC,UAAU,SAAS,cAAc,EAC/C,MAAM,EAAE,UAAU,EAClB,QAAQ,CAAC,EAAE,MAAM,GAChB,kBAAkB,CAAC,UAAU,EAAE,2BAA2B,CAAC;IAI9D,uBAAuB,CAAC,UAAU,SAAS,cAAc,EACvD,MAAM,EAAE,UAAU,EAClB,QAAQ,CAAC,EAAE,MAAM,GAChB,kBAAkB,CAAC,UAAU,EAAE,2BAA2B,CAAC;IAI9D;;;;;;;;;;;OAWG;IACH,UAAU,CAAC,CAAC,SAAS,UAAU,EAAE,KAAK,EAAE,CAAC;IAIzC;;;;;;;;;;;OAWG;IACH,kBAAkB,CAAC,CAAC,SAAS,UAAU,EAAE,KAAK,EAAE,CAAC;IAIjD;;;;;;;;;;;;OAYG;IACH,mBAAmB,CAAC,CAAC,SAAS,UAAU,EAAE,KAAK,EAAE,CAAC,EAAE,QAAQ,CAAC,EAAE,MAAM;IAIrE;;;;;;;;;;;;OAYG;IACH,2BAA2B,CAAC,CAAC,SAAS,UAAU,EAAE,KAAK,EAAE,CAAC,EAAE,QAAQ,CAAC,EAAE,MAAM;IAI7E,uBAAuB,CAAC,YAAY,EAAE,MAAM,OAAO,CAAC,IAAI,CAAC,GAAG,OAAO,CAAC,IAAI,CAAC;IAGzE,qCAAqC,CAAC,CAAC,EAAE,YAAY,EAAE,MAAM,OAAO,CAAC,CAAC,CAAC,GAAG,OAAO,CAAC,CAAC,CAAC;IAGpF;;;;;;OAMG;IACH,uBAAuB,CAAC,YAAY,EAAE,MAAM,OAAO,CAAC,IAAI,CAAC,GAAG,OAAO,CAAC,IAAI,CAAC;IAIzE;;;;;;OAMG;IACH,0CAA0C,CAAC,CAAC,EAAE,YAAY,EAAE,MAAM,OAAO,CAAC,CAAC,CAAC,GAAG,OAAO,CAAC,CAAC,CAAC;IAIzF;;;;;;;;OAQG;IACH,MAAM,CAAC,MAAM,SAAS,UAAU,EAC9B,KAAK,EAAE,MAAM,GACZ,kBAAkB,CAAC,MAAM,EAAE,yBAAyB,EAAE,2BAA2B,CAAC;IAIrF;;;;;;;;OAQG;IACH,mBAAmB,CAAC,MAAM,SAAS,UAAU,EAC3C,KAAK,EAAE,MAAM,GACZ,kBAAkB,CAAC,MAAM,EAAE,yBAAyB,EAAE,2BAA2B,CAAC;IAIrF;;;;;;;;OAQG;IACH,MAAM,CAAC,MAAM,SAAS,UAAU,EAC9B,KAAK,EAAE,MAAM,GACZ,kBAAkB,CAAC,MAAM,EAAE,yBAAyB,EAAE,2BAA2B,CAAC;IAIrF;;;;;;;;OAQG;IACH,mBAAmB,CAAC,MAAM,SAAS,UAAU,EAC3C,KAAK,EAAE,MAAM,GACZ,kBAAkB,CAAC,MAAM,EAAE,yBAAyB,EAAE,2BAA2B,CAAC;IAIrF;;;;;;;;OAQG;IACH,MAAM,CAAC,MAAM,SAAS,UAAU,EAC9B,KAAK,EAAE,MAAM,GACZ,eAAe,CAAC,MAAM,EAAE,yBAAyB,EAAE,2BAA2B,CAAC;IAIlF;;;;;;;;OAQG;IACH,mBAAmB,CAAC,MAAM,SAAS,UAAU,EAC3C,KAAK,EAAE,MAAM,GACZ,eAAe,CAAC,MAAM,EAAE,yBAAyB,EAAE,2BAA2B,CAAC;IAIlF;;;;;;;;;;;;;;;OAeG;IACH,MAAM,CAAC,UAAU,SAAS,cAAc,EACtC,MAAM,EAAE,UAAU,GACjB,kBAAkB,CAAC,UAAU,EAAE,2BAA2B,CAAC;IAI9D;;;;;;;;;;;;;;;OAeG;IACH,cAAc,CAAC,UAAU,SAAS,cAAc,EAC9C,MAAM,EAAE,UAAU,GACjB,kBAAkB,CAAC,UAAU,EAAE,2BAA2B,CAAC;IAI9D;;;OAGG;IACH,oBAAoB,IAAI,6BAA6B;IAIrD;;;OAGG;IACH,KAAK,IAAI,iBAAiB;IAI1B;;;OAGG;IACH,OAAO,IAAI,qBAAqB;IAIhC;;;OAGG;IACH,iCAAiC,IAAI,uBAAuB;IAI5D;;;;OAIG;IACH,sBAAsB;;;;;;;;;;;;;;;;;;IAItB;;;;;;;;;;;;;;;OAeG;IACH,OAAO,CAAC,CAAC,EACP,KAAK,EAAE,UAAU,GAAG,MAAM,GACzB,OAAO,CAAC,oBAAoB,CAAC,yBAAyB,EAAE,CAAC,CAAC,CAAC;IAI9D;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;OAkCG;IACH,UAAU,CAAC,KAAK,EAAE,UAAU,GAAG,MAAM;IAIrC;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;OAiDG;IACH,iBAAiB,CAAC,CAAC,EAAE,OAAO,EAAE,MAAM,OAAO,CAAC,CAAC,CAAC,GAAG,OAAO,CAAC,CAAC,CAAC;IAI3D;;;;;;;;;;;;;;;;;OAiBG;IACH,gBAAgB,CAAC,CAAC,EAChB,KAAK,EAAE,UAAU,GAAG,MAAM,EAC1B,QAAQ,CAAC,EAAE,MAAM,GAChB,OAAO,CAAC,oBAAoB,CAAC,yBAAyB,EAAE,CAAC,CAAC,CAAC;IAI9D;;;;;;;;;;;;;OAaG;IACH,IAAI,KAAK,iDAER;IAED;;;;;;;;;;;;;;;;;;OAkBG;IACH,IAAI,CAAC,GAAG,OAAO,EAAE,YAAY,EAAE;;;;;;;;;;;;IAI/B;;;;;;;;;;;;;;;;;;;;;;;;;;OA0BG;IACH,IAAI,IAAI,eAAe;CAGxB;AAED,eAAe,WAAW,CAAC"}