forge-sql-orm 2.1.13 → 2.1.15

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 (36) hide show
  1. package/README.md +550 -21
  2. package/dist/core/ForgeSQLORM.d.ts +45 -8
  3. package/dist/core/ForgeSQLORM.d.ts.map +1 -1
  4. package/dist/core/ForgeSQLORM.js +134 -15
  5. package/dist/core/ForgeSQLORM.js.map +1 -1
  6. package/dist/core/ForgeSQLQueryBuilder.d.ts +192 -5
  7. package/dist/core/ForgeSQLQueryBuilder.d.ts.map +1 -1
  8. package/dist/core/ForgeSQLQueryBuilder.js.map +1 -1
  9. package/dist/core/Rovo.d.ts +116 -0
  10. package/dist/core/Rovo.d.ts.map +1 -0
  11. package/dist/core/Rovo.js +647 -0
  12. package/dist/core/Rovo.js.map +1 -0
  13. package/dist/utils/forgeDriver.d.ts +3 -2
  14. package/dist/utils/forgeDriver.d.ts.map +1 -1
  15. package/dist/utils/forgeDriver.js +20 -16
  16. package/dist/utils/forgeDriver.js.map +1 -1
  17. package/dist/utils/metadataContextUtils.d.ts +27 -1
  18. package/dist/utils/metadataContextUtils.d.ts.map +1 -1
  19. package/dist/utils/metadataContextUtils.js +215 -12
  20. package/dist/utils/metadataContextUtils.js.map +1 -1
  21. package/dist/webtriggers/index.d.ts +1 -0
  22. package/dist/webtriggers/index.d.ts.map +1 -1
  23. package/dist/webtriggers/index.js +1 -0
  24. package/dist/webtriggers/index.js.map +1 -1
  25. package/dist/webtriggers/topSlowestStatementLastHourTrigger.d.ts +60 -0
  26. package/dist/webtriggers/topSlowestStatementLastHourTrigger.d.ts.map +1 -0
  27. package/dist/webtriggers/topSlowestStatementLastHourTrigger.js +55 -0
  28. package/dist/webtriggers/topSlowestStatementLastHourTrigger.js.map +1 -0
  29. package/package.json +13 -11
  30. package/src/core/ForgeSQLORM.ts +142 -14
  31. package/src/core/ForgeSQLQueryBuilder.ts +213 -4
  32. package/src/core/Rovo.ts +765 -0
  33. package/src/utils/forgeDriver.ts +34 -19
  34. package/src/utils/metadataContextUtils.ts +267 -12
  35. package/src/webtriggers/index.ts +1 -0
  36. package/src/webtriggers/topSlowestStatementLastHourTrigger.ts +69 -0
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "forge-sql-orm",
3
- "version": "2.1.13",
3
+ "version": "2.1.15",
4
4
  "description": "Drizzle ORM integration for Atlassian @forge/sql. Provides a custom driver, schema migration, two levels of caching (local and global via @forge/kvs), optimistic locking, and query analysis.",
5
5
  "main": "dist/index.js",
6
6
  "homepage": "https://github.com/vzakharchenko/forge-sql-orm#readme",
@@ -21,28 +21,29 @@
21
21
  "drizzle-driver",
22
22
  "drizzle-custom-driver",
23
23
  "orm",
24
+ "rovo",
24
25
  "database"
25
26
  ],
26
27
  "devDependencies": {
27
28
  "@eslint/js": "^9.39.1",
28
29
  "@types/luxon": "^3.7.1",
29
30
  "@types/node": "^24.10.1",
30
- "@typescript-eslint/eslint-plugin": "^8.47.0",
31
- "@typescript-eslint/parser": "^8.47.0",
32
- "@vitest/coverage-v8": "^4.0.12",
33
- "@vitest/ui": "^4.0.12",
31
+ "@typescript-eslint/eslint-plugin": "^8.48.0",
32
+ "@typescript-eslint/parser": "^8.48.0",
33
+ "@vitest/coverage-v8": "^4.0.14",
34
+ "@vitest/ui": "^4.0.14",
34
35
  "eslint": "^9.39.1",
35
36
  "eslint-config-prettier": "^10.1.8",
36
37
  "eslint-plugin-import": "^2.32.0",
37
38
  "eslint-plugin-vitest": "^0.5.4",
38
39
  "husky": "^9.1.7",
39
- "knip": "^5.70.1",
40
+ "knip": "^5.70.2",
40
41
  "patch-package": "^8.0.1",
41
- "prettier": "^3.6.2",
42
+ "prettier": "^3.7.2",
42
43
  "ts-node": "^10.9.2",
43
44
  "typescript": "^5.9.3",
44
45
  "uuid": "^13.0.0",
45
- "vitest": "^4.0.12"
46
+ "vitest": "^4.0.14"
46
47
  },
47
48
  "license": "MIT",
48
49
  "author": "Vasyl Zakharchenko",
@@ -70,14 +71,15 @@
70
71
  "README.md"
71
72
  ],
72
73
  "peerDependencies": {
73
- "@forge/sql": "^3.0.10",
74
+ "@forge/sql": "^3.0.12",
74
75
  "drizzle-orm": "^0.44.7"
75
76
  },
76
77
  "optionalDependencies": {
77
- "@forge/kvs": "^1.0.8"
78
+ "@forge/kvs": "^1.2.0"
78
79
  },
79
80
  "dependencies": {
80
- "luxon": "^3.7.2"
81
+ "luxon": "^3.7.2",
82
+ "node-sql-parser": "^5.3.13"
81
83
  },
82
84
  "lint-staged": {
83
85
  "*.{ts,tsx,css,scss,md}": [
@@ -5,6 +5,7 @@ import {
5
5
  ForgeSqlOrmOptions,
6
6
  SchemaAnalyzeForgeSql,
7
7
  SchemaSqlForgeSql,
8
+ RovoIntegration,
8
9
  } from "./ForgeSQLQueryBuilder";
9
10
  import { ForgeSQLSelectOperations } from "./ForgeSQLSelectOperations";
10
11
  import {
@@ -38,9 +39,14 @@ import { cacheApplicationContext, localCacheApplicationContext } from "../utils/
38
39
  import { clearTablesCache } from "../utils/cacheUtils";
39
40
  import { SQLWrapper } from "drizzle-orm/sql/sql";
40
41
  import { WithSubquery } from "drizzle-orm/subquery";
41
- import { getLastestMetadata, metadataQueryContext } from "../utils/metadataContextUtils";
42
+ import {
43
+ getLastestMetadata,
44
+ metadataQueryContext,
45
+ MetadataQueryOptions,
46
+ } from "../utils/metadataContextUtils";
42
47
  import { operationTypeQueryContext } from "../utils/requestTypeContextUtils";
43
48
  import type { MySqlQueryResultKind } from "drizzle-orm/mysql-core/session";
49
+ import { Rovo } from "./Rovo";
44
50
 
45
51
  /**
46
52
  * Implementation of ForgeSQLORM that uses Drizzle ORM for query building.
@@ -122,7 +128,15 @@ class ForgeSQLORMImpl implements ForgeSqlOperation {
122
128
  * @param onMetadata - Callback function that receives aggregated execution metadata
123
129
  * @param onMetadata.totalDbExecutionTime - Total database execution time across all operations in the query function (in milliseconds)
124
130
  * @param onMetadata.totalResponseSize - Total response size across all operations (in bytes)
125
- * @param onMetadata.printQueries - Function to analyze and print query execution plans from CLUSTER_STATEMENTS_SUMMARY
131
+ * @param onMetadata.printQueriesWithPlan - Function to analyze and print query execution plans. Supports two modes:
132
+ * - TopSlowest: Prints execution plans for the slowest queries from the current resolver (default)
133
+ * - SummaryTable: Uses CLUSTER_STATEMENTS_SUMMARY if within time window
134
+ * @param options - Optional configuration for query plan printing behavior
135
+ * @param options.mode - Query plan printing mode: 'TopSlowest' (default) or 'SummaryTable'
136
+ * @param options.summaryTableWindowTime - Time window in milliseconds for summary table queries (default: 15000ms). Only used when mode is 'SummaryTable'
137
+ * @param options.topQueries - Number of top slowest queries to analyze when mode is 'TopSlowest' (default: 1)
138
+ * @param options.showSlowestPlans - Whether to show execution plans for slowest queries in TopSlowest mode (default: true)
139
+ * @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
126
140
  * @returns Promise with the query result
127
141
  *
128
142
  * @example
@@ -134,12 +148,12 @@ class ForgeSQLORMImpl implements ForgeSqlOperation {
134
148
  * const orders = await forgeSQL.selectFrom(ordersTable).where(eq(ordersTable.userId, usersTable.id));
135
149
  * return { users, orders };
136
150
  * },
137
- * (totalDbExecutionTime, totalResponseSize, printQueries) => {
151
+ * (totalDbExecutionTime, totalResponseSize, printQueriesWithPlan) => {
138
152
  * const threshold = 500; // ms baseline for this resolver
139
153
  *
140
154
  * if (totalDbExecutionTime > threshold * 1.5) {
141
155
  * console.warn(`[Performance Warning] Resolver exceeded DB time: ${totalDbExecutionTime} ms`);
142
- * await printQueries(); // Analyze and print query execution plans
156
+ * await printQueriesWithPlan(); // Analyze and print query execution plans
143
157
  * } else if (totalDbExecutionTime > threshold) {
144
158
  * console.debug(`[Performance Debug] High DB time: ${totalDbExecutionTime} ms`);
145
159
  * }
@@ -162,12 +176,12 @@ class ForgeSQLORMImpl implements ForgeSqlOperation {
162
176
  * .where(eq(demoOrders.userId, demoUsers.id));
163
177
  * return { users, orders };
164
178
  * },
165
- * async (totalDbExecutionTime, totalResponseSize, printQueries) => {
179
+ * async (totalDbExecutionTime, totalResponseSize, printQueriesWithPlan) => {
166
180
  * const threshold = 500; // ms baseline for this resolver
167
181
  *
168
182
  * if (totalDbExecutionTime > threshold * 1.5) {
169
183
  * console.warn(`[Performance Warning fetch] Resolver exceeded DB time: ${totalDbExecutionTime} ms`);
170
- * await printQueries(); // Optionally log or capture diagnostics for further analysis
184
+ * await printQueriesWithPlan(); // Optionally log or capture diagnostics for further analysis
171
185
  * } else if (totalDbExecutionTime > threshold) {
172
186
  * console.debug(`[Performance Debug] High DB time: ${totalDbExecutionTime} ms`);
173
187
  * }
@@ -183,7 +197,47 @@ class ForgeSQLORMImpl implements ForgeSqlOperation {
183
197
  * });
184
198
  * ```
185
199
  *
186
- * @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.
200
+ * @example
201
+ * ```typescript
202
+ * // Using TopSlowest mode with custom topQueries
203
+ * const result = await forgeSQL.executeWithMetadata(
204
+ * async () => {
205
+ * const users = await forgeSQL.selectFrom(usersTable);
206
+ * return users;
207
+ * },
208
+ * async (totalDbExecutionTime, totalResponseSize, printQueriesWithPlan) => {
209
+ * if (totalDbExecutionTime > 1000) {
210
+ * await printQueriesWithPlan(); // Will print top 3 slowest queries
211
+ * }
212
+ * },
213
+ * {
214
+ * mode: 'TopSlowest', // Print top slowest queries (default)
215
+ * topQueries: 3, // Print top 3 slowest queries
216
+ * }
217
+ * );
218
+ * ```
219
+ *
220
+ * @example
221
+ * ```typescript
222
+ * // Using SummaryTable mode for query analysis
223
+ * const result = await forgeSQL.executeWithMetadata(
224
+ * async () => {
225
+ * const users = await forgeSQL.selectFrom(usersTable);
226
+ * return users;
227
+ * },
228
+ * async (totalDbExecutionTime, totalResponseSize, printQueriesWithPlan) => {
229
+ * if (totalDbExecutionTime > 1000) {
230
+ * await printQueriesWithPlan(); // Will use CLUSTER_STATEMENTS_SUMMARY if within time window
231
+ * }
232
+ * },
233
+ * {
234
+ * mode: 'SummaryTable', // Use summary tables mode
235
+ * summaryTableWindowTime: 10000, // 10 second window
236
+ * }
237
+ * );
238
+ * ```
239
+ *
240
+ * @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.
187
241
  */
188
242
  async executeWithMetadata<T>(
189
243
  query: () => Promise<T>,
@@ -192,6 +246,7 @@ class ForgeSQLORMImpl implements ForgeSqlOperation {
192
246
  totalResponseSize: number,
193
247
  printQueriesWithPlan: () => Promise<void>,
194
248
  ) => Promise<void> | void,
249
+ options?: MetadataQueryOptions,
195
250
  ): Promise<T> {
196
251
  return metadataQueryContext.run(
197
252
  {
@@ -202,6 +257,8 @@ class ForgeSQLORMImpl implements ForgeSqlOperation {
202
257
  printQueriesWithPlan: async () => {
203
258
  return;
204
259
  },
260
+ options: options,
261
+ statistics: [],
205
262
  },
206
263
  async () => {
207
264
  const result = await query();
@@ -827,6 +884,37 @@ class ForgeSQLORMImpl implements ForgeSqlOperation {
827
884
  with(...queries: WithSubquery[]) {
828
885
  return this.drizzle.with(...queries);
829
886
  }
887
+
888
+ /**
889
+ * Provides access to Rovo integration - a secure pattern for natural-language analytics.
890
+ *
891
+ * Rovo enables secure execution of dynamic SQL queries with comprehensive security validations:
892
+ * - Only SELECT queries are allowed
893
+ * - Queries are restricted to a single table
894
+ * - JOINs, subqueries, and window functions are blocked
895
+ * - Row-Level Security (RLS) support for data isolation
896
+ *
897
+ * @returns {RovoIntegration} Rovo integration instance for secure dynamic queries
898
+ *
899
+ * @example
900
+ * ```typescript
901
+ * const rovo = forgeSQL.rovo();
902
+ * const settings = await rovo.rovoSettingBuilder(usersTable, accountId)
903
+ * .useRLS()
904
+ * .addRlsColumn(usersTable.id)
905
+ * .addRlsWherePart((alias) => `${alias}.id = '${accountId}'`)
906
+ * .finish()
907
+ * .build();
908
+ *
909
+ * const result = await rovo.dynamicIsolatedQuery(
910
+ * "SELECT id, name FROM users WHERE status = 'active'",
911
+ * settings
912
+ * );
913
+ * ```
914
+ */
915
+ rovo(): RovoIntegration {
916
+ return new Rovo(this, this.options);
917
+ }
830
918
  }
831
919
 
832
920
  /**
@@ -853,7 +941,15 @@ class ForgeSQLORM implements ForgeSqlOperation {
853
941
  * @param onMetadata - Callback function that receives aggregated execution metadata
854
942
  * @param onMetadata.totalDbExecutionTime - Total database execution time across all operations in the query function (in milliseconds)
855
943
  * @param onMetadata.totalResponseSize - Total response size across all operations (in bytes)
856
- * @param onMetadata.printQueries - Function to analyze and print query execution plans from CLUSTER_STATEMENTS_SUMMARY
944
+ * @param onMetadata.printQueriesWithPlan - Function to analyze and print query execution plans. Supports two modes:
945
+ * - TopSlowest: Prints execution plans for the slowest queries from the current resolver (default)
946
+ * - SummaryTable: Uses CLUSTER_STATEMENTS_SUMMARY if within time window
947
+ * @param options - Optional configuration for query plan printing behavior
948
+ * @param options.mode - Query plan printing mode: 'TopSlowest' (default) or 'SummaryTable'
949
+ * @param options.summaryTableWindowTime - Time window in milliseconds for summary table queries (default: 15000ms). Only used when mode is 'SummaryTable'
950
+ * @param options.topQueries - Number of top slowest queries to analyze when mode is 'TopSlowest' (default: 1)
951
+ * @param options.showSlowestPlans - Whether to show execution plans for slowest queries in TopSlowest mode (default: true)
952
+ * @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
857
953
  * @returns Promise with the query result
858
954
  *
859
955
  * @example
@@ -865,12 +961,12 @@ class ForgeSQLORM implements ForgeSqlOperation {
865
961
  * const orders = await forgeSQL.selectFrom(ordersTable).where(eq(ordersTable.userId, usersTable.id));
866
962
  * return { users, orders };
867
963
  * },
868
- * (totalDbExecutionTime, totalResponseSize, printQueries) => {
964
+ * (totalDbExecutionTime, totalResponseSize, printQueriesWithPlan) => {
869
965
  * const threshold = 500; // ms baseline for this resolver
870
966
  *
871
967
  * if (totalDbExecutionTime > threshold * 1.5) {
872
968
  * console.warn(`[Performance Warning] Resolver exceeded DB time: ${totalDbExecutionTime} ms`);
873
- * await printQueries(); // Analyze and print query execution plans
969
+ * await printQueriesWithPlan(); // Analyze and print query execution plans
874
970
  * } else if (totalDbExecutionTime > threshold) {
875
971
  * console.debug(`[Performance Debug] High DB time: ${totalDbExecutionTime} ms`);
876
972
  * }
@@ -893,12 +989,12 @@ class ForgeSQLORM implements ForgeSqlOperation {
893
989
  * .where(eq(demoOrders.userId, demoUsers.id));
894
990
  * return { users, orders };
895
991
  * },
896
- * async (totalDbExecutionTime, totalResponseSize, printQueries) => {
992
+ * async (totalDbExecutionTime, totalResponseSize, printQueriesWithPlan) => {
897
993
  * const threshold = 500; // ms baseline for this resolver
898
994
  *
899
995
  * if (totalDbExecutionTime > threshold * 1.5) {
900
996
  * console.warn(`[Performance Warning fetch] Resolver exceeded DB time: ${totalDbExecutionTime} ms`);
901
- * await printQueries(); // Optionally log or capture diagnostics for further analysis
997
+ * await printQueriesWithPlan(); // Optionally log or capture diagnostics for further analysis
902
998
  * } else if (totalDbExecutionTime > threshold) {
903
999
  * console.debug(`[Performance Debug] High DB time: ${totalDbExecutionTime} ms`);
904
1000
  * }
@@ -914,7 +1010,7 @@ class ForgeSQLORM implements ForgeSqlOperation {
914
1010
  * });
915
1011
  * ```
916
1012
  *
917
- * @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.
1013
+ * @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.
918
1014
  */
919
1015
  async executeWithMetadata<T>(
920
1016
  query: () => Promise<T>,
@@ -923,8 +1019,9 @@ class ForgeSQLORM implements ForgeSqlOperation {
923
1019
  totalResponseSize: number,
924
1020
  printQueriesWithPlan: () => Promise<void>,
925
1021
  ) => Promise<void> | void,
1022
+ options?: MetadataQueryOptions,
926
1023
  ): Promise<T> {
927
- return this.ormInstance.executeWithMetadata(query, onMetadata);
1024
+ return this.ormInstance.executeWithMetadata(query, onMetadata, options);
928
1025
  }
929
1026
 
930
1027
  selectCacheable<TSelection extends SelectedFields>(
@@ -1390,6 +1487,37 @@ class ForgeSQLORM implements ForgeSqlOperation {
1390
1487
  with(...queries: WithSubquery[]) {
1391
1488
  return this.ormInstance.getDrizzleQueryBuilder().with(...queries);
1392
1489
  }
1490
+
1491
+ /**
1492
+ * Provides access to Rovo integration - a secure pattern for natural-language analytics.
1493
+ *
1494
+ * Rovo enables secure execution of dynamic SQL queries with comprehensive security validations:
1495
+ * - Only SELECT queries are allowed
1496
+ * - Queries are restricted to a single table
1497
+ * - JOINs, subqueries, and window functions are blocked
1498
+ * - Row-Level Security (RLS) support for data isolation
1499
+ *
1500
+ * @returns {RovoIntegration} Rovo integration instance for secure dynamic queries
1501
+ *
1502
+ * @example
1503
+ * ```typescript
1504
+ * const rovo = forgeSQL.rovo();
1505
+ * const settings = await rovo.rovoSettingBuilder(usersTable, accountId)
1506
+ * .useRLS()
1507
+ * .addRlsColumn(usersTable.id)
1508
+ * .addRlsWherePart((alias) => `${alias}.id = '${accountId}'`)
1509
+ * .finish()
1510
+ * .build();
1511
+ *
1512
+ * const result = await rovo.dynamicIsolatedQuery(
1513
+ * "SELECT id, name FROM users WHERE status = 'active'",
1514
+ * settings
1515
+ * );
1516
+ * ```
1517
+ */
1518
+ rovo(): RovoIntegration {
1519
+ return this.ormInstance.rovo();
1520
+ }
1393
1521
  }
1394
1522
 
1395
1523
  export default ForgeSQLORM;
@@ -1,4 +1,4 @@
1
- import { UpdateQueryResponse } from "@forge/sql";
1
+ import { Result, UpdateQueryResponse } from "@forge/sql";
2
2
  import { SqlParameters } from "@forge/sql/out/sql-statement";
3
3
  import {
4
4
  AnyMySqlSelectQueryBuilder,
@@ -56,6 +56,8 @@ import { SQLWrapper } from "drizzle-orm/sql/sql";
56
56
  import type { MySqlQueryResultKind } from "drizzle-orm/mysql-core/session";
57
57
  import type { WithBuilder } from "drizzle-orm/mysql-core/subquery";
58
58
  import { WithSubquery } from "drizzle-orm/subquery";
59
+ import { MySqlColumn } from "drizzle-orm/mysql-core";
60
+ import { MetadataQueryOptions } from "../utils/metadataContextUtils";
59
61
 
60
62
  /**
61
63
  * Core interface for ForgeSQL operations.
@@ -120,6 +122,35 @@ export interface ForgeSqlOperation extends QueryBuilderForgeSql {
120
122
  * @returns {ForgeSQLCacheOperations} Interface for executing versioned SQL operations with cache management
121
123
  */
122
124
  modifyWithVersioningAndEvictCache(): ForgeSQLCacheOperations;
125
+
126
+ /**
127
+ * Provides access to Rovo integration - a secure pattern for natural-language analytics.
128
+ *
129
+ * Rovo enables secure execution of dynamic SQL queries with comprehensive security validations:
130
+ * - Only SELECT queries are allowed
131
+ * - Queries are restricted to a single table
132
+ * - JOINs, subqueries, and window functions are blocked
133
+ * - Row-Level Security (RLS) support for data isolation
134
+ *
135
+ * @returns {RovoIntegration} Rovo integration instance for secure dynamic queries
136
+ *
137
+ * @example
138
+ * ```typescript
139
+ * const rovo = forgeSQL.rovo();
140
+ * const settings = await rovo.rovoSettingBuilder(usersTable, accountId)
141
+ * .useRLS()
142
+ * .addRlsColumn(usersTable.id)
143
+ * .addRlsWherePart((alias) => `${alias}.id = '${accountId}'`)
144
+ * .finish()
145
+ * .build();
146
+ *
147
+ * const result = await rovo.dynamicIsolatedQuery(
148
+ * "SELECT id, name FROM users WHERE status = 'active'",
149
+ * settings
150
+ * );
151
+ * ```
152
+ */
153
+ rovo(): RovoIntegration;
123
154
  }
124
155
 
125
156
  /**
@@ -555,7 +586,15 @@ export interface QueryBuilderForgeSql {
555
586
  * @param onMetadata - Callback function that receives aggregated execution metadata
556
587
  * @param onMetadata.totalDbExecutionTime - Total database execution time across all operations in the query function (in milliseconds)
557
588
  * @param onMetadata.totalResponseSize - Total response size across all operations (in bytes)
558
- * @param onMetadata.printQueries - Function to analyze and print query execution plans from CLUSTER_STATEMENTS_SUMMARY
589
+ * @param onMetadata.printQueriesWithPlan - Function to analyze and print query execution plans. Supports two modes:
590
+ * - TopSlowest: Prints execution plans for the slowest queries from the current resolver (default)
591
+ * - SummaryTable: Uses CLUSTER_STATEMENTS_SUMMARY if within time window
592
+ * @param options - Optional configuration for query plan printing behavior
593
+ * @param options.mode - Query plan printing mode: 'TopSlowest' (default) or 'SummaryTable'
594
+ * @param options.summaryTableWindowTime - Time window in milliseconds for summary table queries (default: 15000ms). Only used when mode is 'SummaryTable'
595
+ * @param options.topQueries - Number of top slowest queries to analyze when mode is 'TopSlowest' (default: 1)
596
+ * @param options.showSlowestPlans - Whether to show execution plans for slowest queries in TopSlowest mode (default: true)
597
+ * @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
559
598
  * @returns Promise with the query result
560
599
  *
561
600
  * @example
@@ -567,12 +606,12 @@ export interface QueryBuilderForgeSql {
567
606
  * const orders = await forgeSQL.selectFrom(ordersTable).where(eq(ordersTable.userId, usersTable.id));
568
607
  * return { users, orders };
569
608
  * },
570
- * (totalDbExecutionTime, totalResponseSize, printQueries) => {
609
+ * (totalDbExecutionTime, totalResponseSize, printQueriesWithPlan) => {
571
610
  * const threshold = 500; // ms baseline for this resolver
572
611
  *
573
612
  * if (totalDbExecutionTime > threshold * 1.5) {
574
613
  * console.warn(`[Performance Warning] Resolver exceeded DB time: ${totalDbExecutionTime} ms`);
575
- * await printQueries(); // Analyze and print query execution plans
614
+ * await printQueriesWithPlan(); // Analyze and print query execution plans
576
615
  * } else if (totalDbExecutionTime > threshold) {
577
616
  * console.debug(`[Performance Debug] High DB time: ${totalDbExecutionTime} ms`);
578
617
  * }
@@ -625,6 +664,7 @@ export interface QueryBuilderForgeSql {
625
664
  totalResponseSize: number,
626
665
  printQueriesWithPlan: () => Promise<void>,
627
666
  ) => Promise<void> | void,
667
+ options?: MetadataQueryOptions,
628
668
  ): Promise<T>;
629
669
  /**
630
670
  * Executes a raw SQL query with local cache support.
@@ -998,6 +1038,175 @@ export interface SchemaSqlForgeSql {
998
1038
  executeRawUpdateSQL(query: string, params?: unknown[]): Promise<UpdateQueryResponse>;
999
1039
  }
1000
1040
 
1041
+ /**
1042
+ * Interface for Rovo integration settings.
1043
+ * Defines configuration for secure dynamic SQL query execution.
1044
+ *
1045
+ * @interface RovoIntegrationSetting
1046
+ */
1047
+ export interface RovoIntegrationSetting {
1048
+ /**
1049
+ * Gets the account ID of the active user.
1050
+ *
1051
+ * @returns {string} The account ID of the active user
1052
+ */
1053
+ getActiveUser(): string;
1054
+
1055
+ /**
1056
+ * Gets the context parameters for query substitution.
1057
+ *
1058
+ * @returns {Record<string, string>} Map of parameter names to their values
1059
+ */
1060
+ getParameters(): Record<string, string>;
1061
+
1062
+ /**
1063
+ * Gets the name of the table to query.
1064
+ *
1065
+ * @returns {string} The table name
1066
+ */
1067
+ getTableName(): string;
1068
+
1069
+ /**
1070
+ * Checks if Row-Level Security is enabled.
1071
+ *
1072
+ * @returns {boolean} True if RLS is enabled, false otherwise
1073
+ */
1074
+ isUseRLS(): boolean;
1075
+
1076
+ /**
1077
+ * Generates the WHERE clause for Row-Level Security filtering.
1078
+ *
1079
+ * @param {string} alias - The table alias to use in the WHERE clause
1080
+ * @returns {string} SQL WHERE clause condition for RLS filtering
1081
+ */
1082
+ userScopeWhere(alias: string): string;
1083
+
1084
+ /**
1085
+ * Gets the list of field names required for RLS validation.
1086
+ *
1087
+ * @returns {string[]} Array of field names that must be present in SELECT clause for RLS
1088
+ */
1089
+ userScopeFields(): string[];
1090
+ }
1091
+
1092
+ /**
1093
+ * Interface for configuring Row-Level Security (RLS) settings.
1094
+ * Provides a fluent API for setting up RLS conditions, required columns, and WHERE clauses.
1095
+ *
1096
+ * @interface RlsSettings
1097
+ */
1098
+ export interface RlsSettings {
1099
+ /**
1100
+ * Sets a conditional function to determine if RLS should be applied.
1101
+ *
1102
+ * @param {() => Promise<boolean>} condition - Async function that returns true if RLS should be enabled
1103
+ * @returns {RlsSettings} This builder instance for method chaining
1104
+ */
1105
+ addRlsCondition(condition: () => Promise<boolean>): RlsSettings;
1106
+
1107
+ /**
1108
+ * Adds a column name that must be present in the SELECT clause for RLS validation.
1109
+ *
1110
+ * @param {string} columnName - The name of the column to require
1111
+ * @returns {RlsSettings} This builder instance for method chaining
1112
+ */
1113
+ addRlsColumnName(columnName: string): RlsSettings;
1114
+
1115
+ /**
1116
+ * Adds a Drizzle column that must be present in the SELECT clause for RLS validation.
1117
+ *
1118
+ * @param {MySqlColumn} column - The Drizzle column object
1119
+ * @returns {RlsSettings} This builder instance for method chaining
1120
+ */
1121
+ addRlsColumn(columnName: MySqlColumn): RlsSettings;
1122
+
1123
+ /**
1124
+ * Sets the WHERE clause function for RLS filtering.
1125
+ *
1126
+ * @param {(alias: string) => string} wherePart - Function that generates WHERE clause
1127
+ * @returns {RlsSettings} This builder instance for method chaining
1128
+ */
1129
+ addRlsWherePart(wherePart: (alias: string) => string): RlsSettings;
1130
+
1131
+ /**
1132
+ * Finishes RLS configuration and returns to the settings builder.
1133
+ *
1134
+ * @returns {RovoIntegrationSettingCreator} The parent settings builder
1135
+ */
1136
+ finish(): RovoIntegrationSettingCreator;
1137
+ }
1138
+
1139
+ /**
1140
+ * Interface for building Rovo integration settings.
1141
+ * Provides a fluent API for configuring query settings including context parameters and RLS.
1142
+ *
1143
+ * @interface RovoIntegrationSettingCreator
1144
+ */
1145
+ export interface RovoIntegrationSettingCreator {
1146
+ /**
1147
+ * Adds a context parameter for query substitution.
1148
+ *
1149
+ * @param {string} parameterName - The parameter name to replace in the query
1150
+ * @param {string} value - The value to substitute for the parameter
1151
+ * @returns {RovoIntegrationSettingCreator} This builder instance for method chaining
1152
+ */
1153
+ addContextParameter(parameterName: string, value: string): RovoIntegrationSettingCreator;
1154
+
1155
+ /**
1156
+ * Enables Row-Level Security (RLS) for the query.
1157
+ *
1158
+ * @returns {RlsSettings} RLS settings builder for configuring security options
1159
+ */
1160
+ useRLS(): RlsSettings;
1161
+
1162
+ /**
1163
+ * Builds and returns the RovoIntegrationSetting instance.
1164
+ *
1165
+ * @returns {Promise<RovoIntegrationSetting>} The configured RovoIntegrationSetting instance
1166
+ */
1167
+ build(): Promise<RovoIntegrationSetting>;
1168
+ }
1169
+
1170
+ /**
1171
+ * Interface for Rovo integration - a secure pattern for natural-language analytics.
1172
+ *
1173
+ * Rovo provides secure execution of dynamic SQL queries with comprehensive security validations.
1174
+ *
1175
+ * @interface RovoIntegration
1176
+ */
1177
+ export interface RovoIntegration {
1178
+ /**
1179
+ * Creates a settings builder for Rovo queries using a raw table name.
1180
+ *
1181
+ * @param {string} tableName - The name of the table to query
1182
+ * @param {string} accountId - The account ID of the active user
1183
+ * @returns {RovoIntegrationSettingCreator} Builder for configuring Rovo query settings
1184
+ */
1185
+ rovoRawSettingBuilder(tableName: string, accountId: string): RovoIntegrationSettingCreator;
1186
+
1187
+ /**
1188
+ * Creates a settings builder for Rovo queries using a Drizzle table object.
1189
+ *
1190
+ * @param {AnyMySqlTable} table - The Drizzle table object
1191
+ * @param {string} accountId - The account ID of the active user
1192
+ * @returns {RovoIntegrationSettingCreator} Builder for configuring Rovo query settings
1193
+ */
1194
+ rovoSettingBuilder(table: AnyMySqlTable, accountId: string): RovoIntegrationSettingCreator;
1195
+
1196
+ /**
1197
+ * Executes a dynamic SQL query with comprehensive security validations.
1198
+ *
1199
+ * @param {string} dynamicSql - The SQL query to execute (must be a SELECT statement)
1200
+ * @param {RovoIntegrationSetting} settings - Configuration settings for the query
1201
+ * @returns {Promise<Result<unknown>>} Query execution result with metadata
1202
+ * @throws {Error} If the query violates security restrictions
1203
+ */
1204
+ dynamicIsolatedQuery(
1205
+ dynamicSql: string,
1206
+ settings: RovoIntegrationSetting,
1207
+ ): Promise<Result<unknown>>;
1208
+ }
1209
+
1001
1210
  /**
1002
1211
  * Interface for version field metadata.
1003
1212
  * Defines the configuration for optimistic locking version fields.