forge-sql-orm 2.1.15 → 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.
- package/README.md +4 -0
- package/dist/core/ForgeSQLQueryBuilder.d.ts +2 -3
- package/dist/core/ForgeSQLQueryBuilder.d.ts.map +1 -1
- package/dist/core/ForgeSQLQueryBuilder.js.map +1 -1
- package/dist/core/ForgeSQLSelectOperations.d.ts +2 -1
- package/dist/core/ForgeSQLSelectOperations.d.ts.map +1 -1
- package/dist/core/ForgeSQLSelectOperations.js.map +1 -1
- package/dist/core/Rovo.d.ts +40 -0
- package/dist/core/Rovo.d.ts.map +1 -1
- package/dist/core/Rovo.js +164 -138
- package/dist/core/Rovo.js.map +1 -1
- package/dist/index.d.ts +1 -2
- package/dist/index.d.ts.map +1 -1
- package/dist/index.js +3 -2
- package/dist/index.js.map +1 -1
- package/dist/lib/drizzle/extensions/additionalActions.d.ts.map +1 -1
- package/dist/lib/drizzle/extensions/additionalActions.js +72 -22
- package/dist/lib/drizzle/extensions/additionalActions.js.map +1 -1
- package/dist/utils/cacheTableUtils.d.ts +11 -0
- package/dist/utils/cacheTableUtils.d.ts.map +1 -0
- package/dist/utils/cacheTableUtils.js +450 -0
- package/dist/utils/cacheTableUtils.js.map +1 -0
- package/dist/utils/cacheUtils.d.ts.map +1 -1
- package/dist/utils/cacheUtils.js +3 -22
- package/dist/utils/cacheUtils.js.map +1 -1
- package/dist/utils/forgeDriver.d.ts.map +1 -1
- package/dist/utils/forgeDriver.js +5 -12
- package/dist/utils/forgeDriver.js.map +1 -1
- package/dist/utils/metadataContextUtils.d.ts.map +1 -1
- package/dist/utils/metadataContextUtils.js +53 -31
- package/dist/utils/metadataContextUtils.js.map +1 -1
- package/dist/utils/sqlUtils.d.ts +1 -0
- package/dist/utils/sqlUtils.d.ts.map +1 -1
- package/dist/utils/sqlUtils.js +217 -119
- package/dist/utils/sqlUtils.js.map +1 -1
- package/dist/webtriggers/applyMigrationsWebTrigger.js +1 -1
- package/package.json +9 -9
- package/src/core/ForgeSQLQueryBuilder.ts +2 -2
- package/src/core/ForgeSQLSelectOperations.ts +2 -1
- package/src/core/Rovo.ts +209 -167
- package/src/index.ts +1 -3
- package/src/lib/drizzle/extensions/additionalActions.ts +98 -42
- package/src/utils/cacheTableUtils.ts +511 -0
- package/src/utils/cacheUtils.ts +3 -25
- package/src/utils/forgeDriver.ts +5 -11
- package/src/utils/metadataContextUtils.ts +49 -26
- package/src/utils/sqlUtils.ts +298 -142
- package/src/webtriggers/applyMigrationsWebTrigger.ts +1 -1
package/src/utils/cacheUtils.ts
CHANGED
|
@@ -6,6 +6,7 @@ import { getTableName } from "drizzle-orm/table";
|
|
|
6
6
|
import { Filter, FilterConditions, kvs, WhereConditions } from "@forge/kvs";
|
|
7
7
|
import { ForgeSqlOrmOptions } from "../core/ForgeSQLQueryBuilder";
|
|
8
8
|
import { cacheApplicationContext, isTableContainsTableInCacheContext } from "./cacheContextUtils";
|
|
9
|
+
import { extractBacktickedValues } from "./cacheTableUtils";
|
|
9
10
|
|
|
10
11
|
// Constants for better maintainability
|
|
11
12
|
const CACHE_CONSTANTS = {
|
|
@@ -47,29 +48,6 @@ function nowPlusSeconds(secondsToAdd: number): number {
|
|
|
47
48
|
return Math.floor(dt.toSeconds());
|
|
48
49
|
}
|
|
49
50
|
|
|
50
|
-
/**
|
|
51
|
-
* Extracts all table/column names between backticks from SQL query and returns them as comma-separated string.
|
|
52
|
-
*
|
|
53
|
-
* @param sql - SQL query string
|
|
54
|
-
* @returns Comma-separated string of unique backticked values
|
|
55
|
-
*/
|
|
56
|
-
function extractBacktickedValues(sql: string): string {
|
|
57
|
-
const regex = /`([^`]+)`/g;
|
|
58
|
-
const matches = new Set<string>();
|
|
59
|
-
let match;
|
|
60
|
-
|
|
61
|
-
while ((match = regex.exec(sql.toLowerCase())) !== null) {
|
|
62
|
-
if (!match[1].startsWith("a_")) {
|
|
63
|
-
matches.add(`\`${match[1]}\``);
|
|
64
|
-
}
|
|
65
|
-
}
|
|
66
|
-
|
|
67
|
-
// Sort to ensure consistent order for the same input
|
|
68
|
-
return Array.from(matches)
|
|
69
|
-
.sort((a, b) => a.localeCompare(b, undefined, { sensitivity: "base", numeric: true }))
|
|
70
|
-
.join(",");
|
|
71
|
-
}
|
|
72
|
-
|
|
73
51
|
/**
|
|
74
52
|
* Generates a hash key for a query based on its SQL and parameters.
|
|
75
53
|
*
|
|
@@ -362,7 +340,7 @@ export async function getFromCache<T>(
|
|
|
362
340
|
if (
|
|
363
341
|
cacheResult &&
|
|
364
342
|
(cacheResult[expirationName] as number) >= getCurrentTime() &&
|
|
365
|
-
extractBacktickedValues(sqlQuery.sql) === cacheResult[entityQueryName]
|
|
343
|
+
extractBacktickedValues(sqlQuery.sql, options) === cacheResult[entityQueryName]
|
|
366
344
|
) {
|
|
367
345
|
if (options.logCache) {
|
|
368
346
|
// eslint-disable-next-line no-console
|
|
@@ -423,7 +401,7 @@ export async function setCacheResult(
|
|
|
423
401
|
.set(
|
|
424
402
|
key,
|
|
425
403
|
{
|
|
426
|
-
[entityQueryName]: extractBacktickedValues(sqlQuery.sql),
|
|
404
|
+
[entityQueryName]: extractBacktickedValues(sqlQuery.sql, options),
|
|
427
405
|
[expirationName]: nowPlusSeconds(cacheTtl),
|
|
428
406
|
[dataName]: JSON.stringify(results),
|
|
429
407
|
},
|
package/src/utils/forgeDriver.ts
CHANGED
|
@@ -2,6 +2,7 @@ import { sql, UpdateQueryResponse } from "@forge/sql";
|
|
|
2
2
|
import { saveMetaDataToContext } from "./metadataContextUtils";
|
|
3
3
|
import { getOperationType } from "./requestTypeContextUtils";
|
|
4
4
|
import { withTimeout } from "./sqlUtils";
|
|
5
|
+
import { SQL_API_ENDPOINTS } from "@forge/sql/out/sql";
|
|
5
6
|
|
|
6
7
|
const timeoutMs = 10000;
|
|
7
8
|
const timeoutMessage = `Atlassian @forge/sql did not return a response within ${timeoutMs}ms (${timeoutMs / 1000} seconds), so the request is blocked. Possible causes: slow query, network issues, or exceeding Forge SQL limits.`;
|
|
@@ -66,16 +67,6 @@ export function isUpdateQueryResponse(obj: unknown): obj is UpdateQueryResponse
|
|
|
66
67
|
);
|
|
67
68
|
}
|
|
68
69
|
|
|
69
|
-
function inlineParams(sql: string, params: unknown[]): string {
|
|
70
|
-
let i = 0;
|
|
71
|
-
return sql.replace(/\?/g, () => {
|
|
72
|
-
const val = params[i++];
|
|
73
|
-
if (val === null) return "NULL";
|
|
74
|
-
if (typeof val === "number") return val.toString();
|
|
75
|
-
return `'${String(val).replace(/'/g, "''")}'`;
|
|
76
|
-
});
|
|
77
|
-
}
|
|
78
|
-
|
|
79
70
|
/**
|
|
80
71
|
* Processes DDL query results and saves metadata to the execution context.
|
|
81
72
|
*
|
|
@@ -204,7 +195,10 @@ export const forgeDriver = async (
|
|
|
204
195
|
// Handle DDL operations
|
|
205
196
|
if (operationType === "DDL") {
|
|
206
197
|
const result = await withTimeout(
|
|
207
|
-
sql
|
|
198
|
+
sql
|
|
199
|
+
.prepare(query, SQL_API_ENDPOINTS.EXECUTE_DDL)
|
|
200
|
+
.bindParams(params ?? [])
|
|
201
|
+
.execute(),
|
|
208
202
|
timeoutMessage,
|
|
209
203
|
timeoutMs,
|
|
210
204
|
);
|
|
@@ -133,42 +133,65 @@ function normalizeSqlForLogging(sql: string): string {
|
|
|
133
133
|
}
|
|
134
134
|
|
|
135
135
|
/**
|
|
136
|
-
* Formats
|
|
137
|
-
* @param
|
|
138
|
-
* @returns Formatted string
|
|
136
|
+
* Formats row information (estRows, actRows) into a string.
|
|
137
|
+
* @param row - ExplainAnalyzeRow object
|
|
138
|
+
* @returns Formatted row info string or null if no row info available
|
|
139
139
|
*/
|
|
140
|
-
function
|
|
141
|
-
|
|
142
|
-
|
|
143
|
-
}
|
|
140
|
+
function formatRowInfo(row: ExplainAnalyzeRow): string | null {
|
|
141
|
+
const rowInfo: string[] = [];
|
|
142
|
+
if (row.estRows) rowInfo.push(`estRows:${row.estRows}`);
|
|
143
|
+
if (row.actRows) rowInfo.push(`actRows:${row.actRows}`);
|
|
144
|
+
return rowInfo.length > 0 ? `[${rowInfo.join(", ")}]` : null;
|
|
145
|
+
}
|
|
144
146
|
|
|
145
|
-
|
|
147
|
+
/**
|
|
148
|
+
* Formats resource information (memory, disk) into a string.
|
|
149
|
+
* @param row - ExplainAnalyzeRow object
|
|
150
|
+
* @returns Formatted resource info string or null if no resource info available
|
|
151
|
+
*/
|
|
152
|
+
function formatResourceInfo(row: ExplainAnalyzeRow): string | null {
|
|
153
|
+
const resourceInfo: string[] = [];
|
|
154
|
+
if (row.memory) resourceInfo.push(`memory:${row.memory}`);
|
|
155
|
+
if (row.disk) resourceInfo.push(`disk:${row.disk}`);
|
|
156
|
+
return resourceInfo.length > 0 ? `(${resourceInfo.join(", ")})` : null;
|
|
157
|
+
}
|
|
146
158
|
|
|
147
|
-
|
|
148
|
-
|
|
159
|
+
/**
|
|
160
|
+
* Formats a single execution plan row into a string.
|
|
161
|
+
* @param row - ExplainAnalyzeRow object
|
|
162
|
+
* @returns Formatted string representation of the row
|
|
163
|
+
*/
|
|
164
|
+
function formatPlanRow(row: ExplainAnalyzeRow): string {
|
|
165
|
+
const parts: string[] = [];
|
|
166
|
+
|
|
167
|
+
if (row.id) parts.push(row.id);
|
|
168
|
+
if (row.task) parts.push(`task:${row.task}`);
|
|
169
|
+
if (row.operatorInfo) parts.push(row.operatorInfo);
|
|
149
170
|
|
|
150
|
-
|
|
151
|
-
|
|
152
|
-
if (row.operatorInfo) parts.push(row.operatorInfo);
|
|
171
|
+
const rowInfo = formatRowInfo(row);
|
|
172
|
+
if (rowInfo) parts.push(rowInfo);
|
|
153
173
|
|
|
154
|
-
|
|
155
|
-
if (row.estRows) rowInfo.push(`estRows:${row.estRows}`);
|
|
156
|
-
if (row.actRows) rowInfo.push(`actRows:${row.actRows}`);
|
|
157
|
-
if (rowInfo.length > 0) parts.push(`[${rowInfo.join(", ")}]`);
|
|
174
|
+
if (row.executionInfo) parts.push(`execution info:${row.executionInfo}`);
|
|
158
175
|
|
|
159
|
-
|
|
176
|
+
const resourceInfo = formatResourceInfo(row);
|
|
177
|
+
if (resourceInfo) parts.push(resourceInfo);
|
|
160
178
|
|
|
161
|
-
|
|
162
|
-
if (row.memory) resourceInfo.push(`memory:${row.memory}`);
|
|
163
|
-
if (row.disk) resourceInfo.push(`disk:${row.disk}`);
|
|
164
|
-
if (resourceInfo.length > 0) parts.push(`(${resourceInfo.join(", ")})`);
|
|
179
|
+
if (row.accessObject) parts.push(`access object:${row.accessObject}`);
|
|
165
180
|
|
|
166
|
-
|
|
181
|
+
return parts.join(" | ");
|
|
182
|
+
}
|
|
167
183
|
|
|
168
|
-
|
|
184
|
+
/**
|
|
185
|
+
* Formats an execution plan array into a readable string representation.
|
|
186
|
+
* @param planRows - Array of ExplainAnalyzeRow objects representing the execution plan
|
|
187
|
+
* @returns Formatted string representation of the execution plan
|
|
188
|
+
*/
|
|
189
|
+
function formatExplainPlan(planRows: ExplainAnalyzeRow[]): string {
|
|
190
|
+
if (!planRows || planRows.length === 0) {
|
|
191
|
+
return "No execution plan available";
|
|
169
192
|
}
|
|
170
193
|
|
|
171
|
-
return
|
|
194
|
+
return planRows.map(formatPlanRow).join("\n");
|
|
172
195
|
}
|
|
173
196
|
|
|
174
197
|
/**
|
|
@@ -209,7 +232,7 @@ async function printTopQueriesPlans(
|
|
|
209
232
|
options: Required<MetadataQueryOptions>,
|
|
210
233
|
): Promise<void> {
|
|
211
234
|
const topQueries = context.statistics
|
|
212
|
-
.
|
|
235
|
+
.toSorted((a, b) => b.metadata.dbExecutionTime - a.metadata.dbExecutionTime)
|
|
213
236
|
.slice(0, options.topQueries);
|
|
214
237
|
|
|
215
238
|
for (const query of topQueries) {
|