forge-sql-orm 2.1.4 → 2.1.6
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 +195 -27
- package/dist/ForgeSQLORM.js +632 -192
- package/dist/ForgeSQLORM.js.map +1 -1
- package/dist/ForgeSQLORM.mjs +632 -192
- package/dist/ForgeSQLORM.mjs.map +1 -1
- package/dist/core/ForgeSQLCrudOperations.d.ts.map +1 -1
- package/dist/core/ForgeSQLORM.d.ts +114 -3
- package/dist/core/ForgeSQLORM.d.ts.map +1 -1
- package/dist/core/ForgeSQLQueryBuilder.d.ts +125 -7
- package/dist/core/ForgeSQLQueryBuilder.d.ts.map +1 -1
- package/dist/core/ForgeSQLSelectOperations.d.ts.map +1 -1
- package/dist/core/SystemTables.d.ts +3654 -0
- package/dist/core/SystemTables.d.ts.map +1 -1
- package/dist/lib/drizzle/extensions/additionalActions.d.ts +2 -2
- package/dist/lib/drizzle/extensions/additionalActions.d.ts.map +1 -1
- package/dist/utils/cacheContextUtils.d.ts.map +1 -1
- package/dist/utils/cacheUtils.d.ts.map +1 -1
- package/dist/utils/forgeDriver.d.ts +71 -3
- package/dist/utils/forgeDriver.d.ts.map +1 -1
- package/dist/utils/forgeDriverProxy.d.ts.map +1 -1
- package/dist/utils/metadataContextUtils.d.ts +11 -0
- package/dist/utils/metadataContextUtils.d.ts.map +1 -0
- package/dist/utils/requestTypeContextUtils.d.ts +8 -0
- package/dist/utils/requestTypeContextUtils.d.ts.map +1 -0
- package/dist/utils/sqlUtils.d.ts.map +1 -1
- package/dist/webtriggers/applyMigrationsWebTrigger.d.ts.map +1 -1
- package/dist/webtriggers/clearCacheSchedulerTrigger.d.ts.map +1 -1
- package/dist/webtriggers/dropMigrationWebTrigger.d.ts.map +1 -1
- package/dist/webtriggers/dropTablesMigrationWebTrigger.d.ts.map +1 -1
- package/dist/webtriggers/fetchSchemaWebTrigger.d.ts.map +1 -1
- package/dist/webtriggers/topSlowestStatementLastHourTrigger.d.ts +85 -43
- package/dist/webtriggers/topSlowestStatementLastHourTrigger.d.ts.map +1 -1
- package/package.json +9 -9
- package/src/core/ForgeSQLCrudOperations.ts +3 -0
- package/src/core/ForgeSQLORM.ts +287 -9
- package/src/core/ForgeSQLQueryBuilder.ts +138 -8
- package/src/core/ForgeSQLSelectOperations.ts +2 -0
- package/src/core/SystemTables.ts +16 -0
- package/src/lib/drizzle/extensions/additionalActions.ts +10 -12
- package/src/utils/cacheContextUtils.ts +4 -2
- package/src/utils/cacheUtils.ts +20 -8
- package/src/utils/forgeDriver.ts +223 -23
- package/src/utils/forgeDriverProxy.ts +2 -0
- package/src/utils/metadataContextUtils.ts +22 -0
- package/src/utils/requestTypeContextUtils.ts +11 -0
- package/src/utils/sqlUtils.ts +1 -0
- package/src/webtriggers/applyMigrationsWebTrigger.ts +9 -6
- package/src/webtriggers/clearCacheSchedulerTrigger.ts +1 -0
- package/src/webtriggers/dropMigrationWebTrigger.ts +2 -0
- package/src/webtriggers/dropTablesMigrationWebTrigger.ts +2 -0
- package/src/webtriggers/fetchSchemaWebTrigger.ts +1 -0
- package/src/webtriggers/topSlowestStatementLastHourTrigger.ts +515 -257
|
@@ -102,8 +102,9 @@ export async function saveQueryLocalCacheQuery<
|
|
|
102
102
|
sql: sql.toSQL().sql.toLowerCase(),
|
|
103
103
|
data: rows,
|
|
104
104
|
};
|
|
105
|
-
if (options.
|
|
105
|
+
if (options.logCache) {
|
|
106
106
|
const q = sql.toSQL();
|
|
107
|
+
// eslint-disable-next-line no-console
|
|
107
108
|
console.debug(
|
|
108
109
|
`[forge-sql-orm][local-cache][SAVE] Stored result in cache. sql="${q.sql}", params=${JSON.stringify(q.params)}`,
|
|
109
110
|
);
|
|
@@ -140,8 +141,9 @@ export async function getQueryLocalCacheQuery<
|
|
|
140
141
|
const sql = query as { toSQL: () => Query };
|
|
141
142
|
const key = hashKey(sql.toSQL());
|
|
142
143
|
if (context.cache[key] && context.cache[key].sql === sql.toSQL().sql.toLowerCase()) {
|
|
143
|
-
if (options.
|
|
144
|
+
if (options.logCache) {
|
|
144
145
|
const q = sql.toSQL();
|
|
146
|
+
// eslint-disable-next-line no-console
|
|
145
147
|
console.debug(
|
|
146
148
|
`[forge-sql-orm][local-cache][HIT] Returned cached result. sql="${q.sql}", params=${JSON.stringify(q.params)}`,
|
|
147
149
|
);
|
package/src/utils/cacheUtils.ts
CHANGED
|
@@ -123,7 +123,8 @@ async function clearCursorCache(
|
|
|
123
123
|
|
|
124
124
|
const listResult = await entityQueryBuilder.limit(100).getMany();
|
|
125
125
|
|
|
126
|
-
if (options.
|
|
126
|
+
if (options.logCache) {
|
|
127
|
+
// eslint-disable-next-line no-console
|
|
127
128
|
console.warn(`clear cache Records: ${JSON.stringify(listResult.results.map((r) => r.key))}`);
|
|
128
129
|
}
|
|
129
130
|
|
|
@@ -170,7 +171,8 @@ async function clearExpirationCursorCache(
|
|
|
170
171
|
|
|
171
172
|
const listResult = await entityQueryBuilder.limit(100).getMany();
|
|
172
173
|
|
|
173
|
-
if (options.
|
|
174
|
+
if (options.logCache) {
|
|
175
|
+
// eslint-disable-next-line no-console
|
|
174
176
|
console.warn(`clear expired Records: ${JSON.stringify(listResult.results.map((r) => r.key))}`);
|
|
175
177
|
}
|
|
176
178
|
|
|
@@ -201,10 +203,12 @@ async function executeWithRetry<T>(operation: () => Promise<T>, operationName: s
|
|
|
201
203
|
try {
|
|
202
204
|
return await operation();
|
|
203
205
|
} catch (err: any) {
|
|
206
|
+
// eslint-disable-next-line no-console
|
|
204
207
|
console.warn(`Error during ${operationName}: ${err.message}, retry ${attempt}`, err);
|
|
205
208
|
attempt++;
|
|
206
209
|
|
|
207
210
|
if (attempt >= CACHE_CONSTANTS.MAX_RETRY_ATTEMPTS) {
|
|
211
|
+
// eslint-disable-next-line no-console
|
|
208
212
|
console.error(`Error during ${operationName}: ${err.message}`, err);
|
|
209
213
|
throw err;
|
|
210
214
|
}
|
|
@@ -260,8 +264,9 @@ export async function clearTablesCache(
|
|
|
260
264
|
"clearing cache",
|
|
261
265
|
);
|
|
262
266
|
} finally {
|
|
263
|
-
if (options.
|
|
267
|
+
if (options.logCache) {
|
|
264
268
|
const duration = DateTime.now().toSeconds() - startTime.toSeconds();
|
|
269
|
+
// eslint-disable-next-line no-console
|
|
265
270
|
console.info(`Cleared ${totalRecords} cache records in ${duration} seconds`);
|
|
266
271
|
}
|
|
267
272
|
}
|
|
@@ -287,7 +292,8 @@ export async function clearExpiredCache(options: ForgeSqlOrmOptions): Promise<vo
|
|
|
287
292
|
);
|
|
288
293
|
} finally {
|
|
289
294
|
const duration = DateTime.now().toSeconds() - startTime.toSeconds();
|
|
290
|
-
if (options?.
|
|
295
|
+
if (options?.logCache) {
|
|
296
|
+
// eslint-disable-next-line no-console
|
|
291
297
|
console.debug(`Cleared ${totalRecords} expired cache records in ${duration} seconds`);
|
|
292
298
|
}
|
|
293
299
|
}
|
|
@@ -318,7 +324,8 @@ export async function getFromCache<T>(
|
|
|
318
324
|
|
|
319
325
|
// Skip cache if table is in cache context (will be cleared)
|
|
320
326
|
if (await isTableContainsTableInCacheContext(sqlQuery.sql, options)) {
|
|
321
|
-
if (options.
|
|
327
|
+
if (options.logCache) {
|
|
328
|
+
// eslint-disable-next-line no-console
|
|
322
329
|
console.warn(`Context contains value to clear. Skip getting from cache`);
|
|
323
330
|
}
|
|
324
331
|
return undefined;
|
|
@@ -332,13 +339,15 @@ export async function getFromCache<T>(
|
|
|
332
339
|
(cacheResult[expirationName] as number) >= getCurrentTime() &&
|
|
333
340
|
sqlQuery.sql.toLowerCase() === cacheResult[entityQueryName]
|
|
334
341
|
) {
|
|
335
|
-
if (options.
|
|
342
|
+
if (options.logCache) {
|
|
343
|
+
// eslint-disable-next-line no-console
|
|
336
344
|
console.warn(`Get value from cache, cacheKey: ${key}`);
|
|
337
345
|
}
|
|
338
346
|
const results = cacheResult[dataName];
|
|
339
347
|
return JSON.parse(results as string);
|
|
340
348
|
}
|
|
341
349
|
} catch (error: any) {
|
|
350
|
+
// eslint-disable-next-line no-console
|
|
342
351
|
console.error(`Error getting from cache: ${error.message}`, error);
|
|
343
352
|
}
|
|
344
353
|
|
|
@@ -375,7 +384,8 @@ export async function setCacheResult(
|
|
|
375
384
|
|
|
376
385
|
// Skip cache if table is in cache context (will be cleared)
|
|
377
386
|
if (await isTableContainsTableInCacheContext(sqlQuery.sql, options)) {
|
|
378
|
-
if (options.
|
|
387
|
+
if (options.logCache) {
|
|
388
|
+
// eslint-disable-next-line no-console
|
|
379
389
|
console.warn(`Context contains value to clear. Skip setting from cache`);
|
|
380
390
|
}
|
|
381
391
|
return;
|
|
@@ -396,10 +406,12 @@ export async function setCacheResult(
|
|
|
396
406
|
)
|
|
397
407
|
.execute();
|
|
398
408
|
|
|
399
|
-
if (options.
|
|
409
|
+
if (options.logCache) {
|
|
410
|
+
// eslint-disable-next-line no-console
|
|
400
411
|
console.warn(`Store value to cache, cacheKey: ${key}`);
|
|
401
412
|
}
|
|
402
413
|
} catch (error: any) {
|
|
414
|
+
// eslint-disable-next-line no-console
|
|
403
415
|
console.error(`Error setting cache: ${error.message}`, error);
|
|
404
416
|
}
|
|
405
417
|
}
|
package/src/utils/forgeDriver.ts
CHANGED
|
@@ -1,34 +1,234 @@
|
|
|
1
1
|
import { sql, UpdateQueryResponse } from "@forge/sql";
|
|
2
|
+
import { saveMetaDataToContext } from "./metadataContextUtils";
|
|
3
|
+
import { getOperationType } from "./requestTypeContextUtils";
|
|
2
4
|
|
|
3
|
-
|
|
5
|
+
/**
|
|
6
|
+
* Metadata structure for Forge SQL query results.
|
|
7
|
+
* Contains execution timing, response size, and field information.
|
|
8
|
+
*/
|
|
9
|
+
export type ForgeSQLMetadata = {
|
|
10
|
+
dbExecutionTime: number;
|
|
11
|
+
responseSize: number;
|
|
12
|
+
fields: {
|
|
13
|
+
catalog: string;
|
|
14
|
+
name: string;
|
|
15
|
+
schema: string;
|
|
16
|
+
characterSet: number;
|
|
17
|
+
decimals: number;
|
|
18
|
+
table: string;
|
|
19
|
+
orgTable: string;
|
|
20
|
+
orgName: string;
|
|
21
|
+
flags: number;
|
|
22
|
+
columnType: number;
|
|
23
|
+
columnLength: number;
|
|
24
|
+
}[];
|
|
25
|
+
};
|
|
26
|
+
|
|
27
|
+
/**
|
|
28
|
+
* Result structure for Forge SQL queries.
|
|
29
|
+
* Contains rows data and execution metadata.
|
|
30
|
+
*/
|
|
31
|
+
export interface ForgeSQLResult {
|
|
4
32
|
rows: Record<string, unknown>[] | Record<string, unknown>;
|
|
33
|
+
metadata: ForgeSQLMetadata;
|
|
5
34
|
}
|
|
6
35
|
|
|
7
|
-
|
|
8
|
-
|
|
9
|
-
|
|
10
|
-
|
|
11
|
-
|
|
12
|
-
rows: any[];
|
|
36
|
+
/**
|
|
37
|
+
* Driver result structure for Drizzle ORM compatibility.
|
|
38
|
+
*/
|
|
39
|
+
export interface ForgeDriverResult {
|
|
40
|
+
rows: unknown[];
|
|
13
41
|
insertId?: number;
|
|
14
42
|
affectedRows?: number;
|
|
15
|
-
}
|
|
16
|
-
|
|
17
|
-
|
|
18
|
-
|
|
19
|
-
|
|
43
|
+
}
|
|
44
|
+
|
|
45
|
+
/**
|
|
46
|
+
* Query execution method types.
|
|
47
|
+
*/
|
|
48
|
+
export type QueryMethod = "all" | "execute";
|
|
49
|
+
|
|
50
|
+
/**
|
|
51
|
+
* Type guard to check if an object is an UpdateQueryResponse.
|
|
52
|
+
*
|
|
53
|
+
* @param obj - The object to check
|
|
54
|
+
* @returns True if the object is an UpdateQueryResponse
|
|
55
|
+
*/
|
|
56
|
+
export function isUpdateQueryResponse(obj: unknown): obj is UpdateQueryResponse {
|
|
57
|
+
return (
|
|
58
|
+
obj !== null &&
|
|
59
|
+
typeof obj === "object" &&
|
|
60
|
+
typeof (obj as any).affectedRows === "number" &&
|
|
61
|
+
typeof (obj as any).insertId === "number"
|
|
62
|
+
);
|
|
63
|
+
}
|
|
64
|
+
|
|
65
|
+
/**
|
|
66
|
+
* Executes a promise with a timeout.
|
|
67
|
+
*
|
|
68
|
+
* @param promise - The promise to execute
|
|
69
|
+
* @param timeoutMs - Timeout in milliseconds (default: 10000ms)
|
|
70
|
+
* @returns Promise that resolves with the result or rejects on timeout
|
|
71
|
+
* @throws {Error} When the operation times out
|
|
72
|
+
*/
|
|
73
|
+
async function withTimeout<T>(promise: Promise<T>, timeoutMs: number = 10000): Promise<T> {
|
|
74
|
+
let timeoutId: ReturnType<typeof setTimeout> | undefined;
|
|
75
|
+
|
|
76
|
+
const timeoutPromise = new Promise<never>((_, reject) => {
|
|
77
|
+
timeoutId = setTimeout(() => {
|
|
78
|
+
reject(
|
|
79
|
+
new Error(
|
|
80
|
+
`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.`,
|
|
81
|
+
),
|
|
82
|
+
);
|
|
83
|
+
}, timeoutMs);
|
|
84
|
+
});
|
|
85
|
+
|
|
86
|
+
try {
|
|
87
|
+
return await Promise.race([promise, timeoutPromise]);
|
|
88
|
+
} finally {
|
|
89
|
+
if (timeoutId) {
|
|
90
|
+
clearTimeout(timeoutId);
|
|
20
91
|
}
|
|
21
|
-
|
|
22
|
-
|
|
23
|
-
|
|
24
|
-
|
|
25
|
-
|
|
26
|
-
|
|
27
|
-
|
|
92
|
+
}
|
|
93
|
+
}
|
|
94
|
+
|
|
95
|
+
function inlineParams(sql: string, params: unknown[]): string {
|
|
96
|
+
let i = 0;
|
|
97
|
+
return sql.replace(/\?/g, () => {
|
|
98
|
+
const val = params[i++];
|
|
99
|
+
if (val === null) return "NULL";
|
|
100
|
+
if (typeof val === "number") return val.toString();
|
|
101
|
+
return `'${String(val).replace(/'/g, "''")}'`;
|
|
102
|
+
});
|
|
103
|
+
}
|
|
104
|
+
|
|
105
|
+
/**
|
|
106
|
+
* Processes DDL query results and saves metadata.
|
|
107
|
+
*
|
|
108
|
+
* @param result - The DDL query result
|
|
109
|
+
* @returns Processed result for Drizzle ORM
|
|
110
|
+
*/
|
|
111
|
+
async function processDDLResult(method: QueryMethod, result: any): Promise<ForgeDriverResult> {
|
|
112
|
+
if (result.metadata) {
|
|
113
|
+
await saveMetaDataToContext(result.metadata as ForgeSQLMetadata);
|
|
114
|
+
}
|
|
115
|
+
|
|
116
|
+
if (!result.rows) {
|
|
117
|
+
return { rows: [] };
|
|
118
|
+
}
|
|
119
|
+
|
|
120
|
+
if (isUpdateQueryResponse(result.rows)) {
|
|
121
|
+
const oneRow = result.rows as any;
|
|
122
|
+
return { ...oneRow, rows: [oneRow] };
|
|
123
|
+
}
|
|
124
|
+
|
|
125
|
+
if (Array.isArray(result.rows)) {
|
|
126
|
+
if (method === "execute") {
|
|
127
|
+
return { rows: result.rows };
|
|
128
|
+
} else {
|
|
129
|
+
const rows = (result.rows as any[]).map((r) => Object.values(r as Record<string, unknown>));
|
|
130
|
+
return { rows };
|
|
28
131
|
}
|
|
29
|
-
const result = (await sqlStatement.execute()) as ForgeSQLResult;
|
|
30
|
-
let rows;
|
|
31
|
-
rows = (result.rows as any[]).map((r) => Object.values(r as Record<string, unknown>));
|
|
32
|
-
return { rows: rows };
|
|
33
132
|
}
|
|
133
|
+
|
|
134
|
+
return { rows: [] };
|
|
135
|
+
}
|
|
136
|
+
|
|
137
|
+
/**
|
|
138
|
+
* Processes execute method results (UPDATE, INSERT, DELETE).
|
|
139
|
+
*
|
|
140
|
+
* @param query - The SQL query
|
|
141
|
+
* @param params - Query parameters
|
|
142
|
+
* @returns Processed result for Drizzle ORM
|
|
143
|
+
*/
|
|
144
|
+
async function processExecuteMethod(query: string, params: unknown[]): Promise<ForgeDriverResult> {
|
|
145
|
+
const sqlStatement = sql.prepare<UpdateQueryResponse>(query);
|
|
146
|
+
|
|
147
|
+
if (params) {
|
|
148
|
+
sqlStatement.bindParams(...params);
|
|
149
|
+
}
|
|
150
|
+
|
|
151
|
+
const result = await withTimeout(sqlStatement.execute());
|
|
152
|
+
await saveMetaDataToContext(result.metadata as ForgeSQLMetadata);
|
|
153
|
+
if (!result.rows) {
|
|
154
|
+
return { rows: [] };
|
|
155
|
+
}
|
|
156
|
+
|
|
157
|
+
if (isUpdateQueryResponse(result.rows)) {
|
|
158
|
+
const oneRow = result.rows as any;
|
|
159
|
+
return { ...oneRow, rows: [oneRow] };
|
|
160
|
+
}
|
|
161
|
+
|
|
162
|
+
return { rows: result.rows };
|
|
163
|
+
}
|
|
164
|
+
|
|
165
|
+
/**
|
|
166
|
+
* Processes all method results (SELECT queries).
|
|
167
|
+
*
|
|
168
|
+
* @param query - The SQL query
|
|
169
|
+
* @param params - Query parameters
|
|
170
|
+
* @returns Processed result for Drizzle ORM
|
|
171
|
+
*/
|
|
172
|
+
async function processAllMethod(query: string, params: unknown[]): Promise<ForgeDriverResult> {
|
|
173
|
+
const sqlStatement = await sql.prepare<unknown>(query);
|
|
174
|
+
|
|
175
|
+
if (params) {
|
|
176
|
+
await sqlStatement.bindParams(...params);
|
|
177
|
+
}
|
|
178
|
+
|
|
179
|
+
const result = (await withTimeout(sqlStatement.execute())) as ForgeSQLResult;
|
|
180
|
+
await saveMetaDataToContext(result.metadata);
|
|
181
|
+
|
|
182
|
+
if (!result.rows) {
|
|
183
|
+
return { rows: [] };
|
|
184
|
+
}
|
|
185
|
+
|
|
186
|
+
const rows = (result.rows as any[]).map((r) => Object.values(r as Record<string, unknown>));
|
|
187
|
+
|
|
188
|
+
return { rows };
|
|
189
|
+
}
|
|
190
|
+
|
|
191
|
+
/**
|
|
192
|
+
* Main Forge SQL driver function for Drizzle ORM integration.
|
|
193
|
+
* Handles DDL operations, execute operations (UPDATE/INSERT/DELETE), and select operations.
|
|
194
|
+
*
|
|
195
|
+
* @param query - The SQL query to execute
|
|
196
|
+
* @param params - Query parameters
|
|
197
|
+
* @param method - Execution method ("all" for SELECT, "execute" for UPDATE/INSERT/DELETE)
|
|
198
|
+
* @returns Promise with query results compatible with Drizzle ORM
|
|
199
|
+
*
|
|
200
|
+
* @throws {Error} When DDL operations are called with parameters
|
|
201
|
+
*
|
|
202
|
+
* @example
|
|
203
|
+
* ```typescript
|
|
204
|
+
* // DDL operation
|
|
205
|
+
* await forgeDriver("CREATE TABLE users (id INT)", [], "all");
|
|
206
|
+
*
|
|
207
|
+
* // SELECT operation
|
|
208
|
+
* await forgeDriver("SELECT * FROM users WHERE id = ?", [1], "all");
|
|
209
|
+
*
|
|
210
|
+
* // UPDATE operation
|
|
211
|
+
* await forgeDriver("UPDATE users SET name = ? WHERE id = ?", ["John", 1], "execute");
|
|
212
|
+
* ```
|
|
213
|
+
*/
|
|
214
|
+
export const forgeDriver = async (
|
|
215
|
+
query: string,
|
|
216
|
+
params: unknown[],
|
|
217
|
+
method: QueryMethod,
|
|
218
|
+
): Promise<ForgeDriverResult> => {
|
|
219
|
+
const operationType = await getOperationType();
|
|
220
|
+
|
|
221
|
+
// Handle DDL operations
|
|
222
|
+
if (operationType === "DDL") {
|
|
223
|
+
const result = await withTimeout(sql.executeDDL(inlineParams(query, params)));
|
|
224
|
+
return await processDDLResult(method, result);
|
|
225
|
+
}
|
|
226
|
+
|
|
227
|
+
// Handle execute method (UPDATE, INSERT, DELETE)
|
|
228
|
+
if (method === "execute") {
|
|
229
|
+
return await processExecuteMethod(query, params ?? []);
|
|
230
|
+
}
|
|
231
|
+
|
|
232
|
+
// Handle all method (SELECT)
|
|
233
|
+
return await processAllMethod(query, params ?? []);
|
|
34
234
|
};
|
|
@@ -19,6 +19,7 @@ export function createForgeDriverProxy(options?: SqlHints, logRawSqlQuery?: bool
|
|
|
19
19
|
const modifiedQuery = injectSqlHints(query, options);
|
|
20
20
|
|
|
21
21
|
if (options && logRawSqlQuery && modifiedQuery !== query) {
|
|
22
|
+
// eslint-disable-next-line no-console
|
|
22
23
|
console.debug("injected Hints: " + modifiedQuery);
|
|
23
24
|
}
|
|
24
25
|
try {
|
|
@@ -26,6 +27,7 @@ export function createForgeDriverProxy(options?: SqlHints, logRawSqlQuery?: bool
|
|
|
26
27
|
return await forgeDriver(modifiedQuery, params, method);
|
|
27
28
|
} catch (error) {
|
|
28
29
|
if (logRawSqlQuery) {
|
|
30
|
+
// eslint-disable-next-line no-console
|
|
29
31
|
console.debug("SQL Error:", JSON.stringify(error));
|
|
30
32
|
}
|
|
31
33
|
throw error;
|
|
@@ -0,0 +1,22 @@
|
|
|
1
|
+
import { AsyncLocalStorage } from "node:async_hooks";
|
|
2
|
+
import { ForgeSQLMetadata } from "./forgeDriver";
|
|
3
|
+
|
|
4
|
+
export type MetadataQueryContext = {
|
|
5
|
+
totalDbExecutionTime: number;
|
|
6
|
+
totalResponseSize: number;
|
|
7
|
+
lastMetadata?: ForgeSQLMetadata;
|
|
8
|
+
};
|
|
9
|
+
export const metadataQueryContext = new AsyncLocalStorage<MetadataQueryContext>();
|
|
10
|
+
|
|
11
|
+
export async function saveMetaDataToContext(metadata: ForgeSQLMetadata): Promise<void> {
|
|
12
|
+
const context = metadataQueryContext.getStore();
|
|
13
|
+
if (context && metadata) {
|
|
14
|
+
context.totalResponseSize += metadata.responseSize;
|
|
15
|
+
context.totalDbExecutionTime += metadata.dbExecutionTime;
|
|
16
|
+
context.lastMetadata = metadata;
|
|
17
|
+
}
|
|
18
|
+
}
|
|
19
|
+
|
|
20
|
+
export async function getLastestMetadata(): Promise<MetadataQueryContext | undefined> {
|
|
21
|
+
return metadataQueryContext.getStore();
|
|
22
|
+
}
|
|
@@ -0,0 +1,11 @@
|
|
|
1
|
+
import { AsyncLocalStorage } from "node:async_hooks";
|
|
2
|
+
export type OperationType = "DML" | "DDL";
|
|
3
|
+
|
|
4
|
+
export type OperationTypeQueryContext = {
|
|
5
|
+
operationType: OperationType;
|
|
6
|
+
};
|
|
7
|
+
export const operationTypeQueryContext = new AsyncLocalStorage<OperationTypeQueryContext>();
|
|
8
|
+
|
|
9
|
+
export async function getOperationType(): Promise<OperationType> {
|
|
10
|
+
return operationTypeQueryContext.getStore()?.operationType ?? "DML";
|
|
11
|
+
}
|
package/src/utils/sqlUtils.ts
CHANGED
|
@@ -329,6 +329,7 @@ export function generateDropTableStatements(
|
|
|
329
329
|
const dropStatements: string[] = [];
|
|
330
330
|
const validOptions = options ?? { sequence: true, table: true };
|
|
331
331
|
if (!validOptions.sequence && !validOptions.table) {
|
|
332
|
+
// eslint-disable-next-line no-console
|
|
332
333
|
console.warn('No drop operations requested: both "table" and "sequence" options are false');
|
|
333
334
|
return [];
|
|
334
335
|
}
|
|
@@ -31,13 +31,15 @@ export const applySchemaMigrations = async (
|
|
|
31
31
|
if (typeof migration !== "function") {
|
|
32
32
|
throw new Error("migration is not a function");
|
|
33
33
|
}
|
|
34
|
-
|
|
35
|
-
console.
|
|
34
|
+
// eslint-disable-next-line no-console
|
|
35
|
+
console.debug("Provisioning the database");
|
|
36
36
|
await sql._provision();
|
|
37
|
-
|
|
37
|
+
// eslint-disable-next-line no-console
|
|
38
|
+
console.debug("Running schema migrations");
|
|
38
39
|
const migrations = await migration(migrationRunner);
|
|
39
40
|
const successfulMigrations = await migrations.run();
|
|
40
|
-
|
|
41
|
+
// eslint-disable-next-line no-console
|
|
42
|
+
console.debug("Migrations applied:", successfulMigrations);
|
|
41
43
|
|
|
42
44
|
const migrationList = await migrationRunner.list();
|
|
43
45
|
let migrationHistory = "No migrations found";
|
|
@@ -51,8 +53,8 @@ export const applySchemaMigrations = async (
|
|
|
51
53
|
.map((y) => `${y.id}, ${y.name}, ${y.migratedAt.toUTCString()}`)
|
|
52
54
|
.join("\n");
|
|
53
55
|
}
|
|
54
|
-
|
|
55
|
-
console.
|
|
56
|
+
// eslint-disable-next-line no-console
|
|
57
|
+
console.debug("Migrations history:\nid, name, migrated_at\n", migrationHistory);
|
|
56
58
|
|
|
57
59
|
return {
|
|
58
60
|
headers: { "Content-Type": ["application/json"] },
|
|
@@ -68,6 +70,7 @@ export const applySchemaMigrations = async (
|
|
|
68
70
|
error?.debug?.context?.message ??
|
|
69
71
|
error.message ??
|
|
70
72
|
"Unknown error occurred";
|
|
73
|
+
// eslint-disable-next-line no-console
|
|
71
74
|
console.error("Error during migration:", errorMessage);
|
|
72
75
|
return {
|
|
73
76
|
headers: { "Content-Type": ["application/json"] },
|
|
@@ -64,6 +64,7 @@ export const clearCacheSchedulerTrigger = async (options?: ForgeSqlOrmOptions) =
|
|
|
64
64
|
}),
|
|
65
65
|
};
|
|
66
66
|
} catch (error) {
|
|
67
|
+
// eslint-disable-next-line no-console
|
|
67
68
|
console.error("Error during cache cleanup: ", JSON.stringify(error));
|
|
68
69
|
return {
|
|
69
70
|
headers: { "Content-Type": ["application/json"] },
|
|
@@ -34,6 +34,7 @@ export async function dropSchemaMigrations(): Promise<TriggerResponse<string>> {
|
|
|
34
34
|
|
|
35
35
|
// Execute each statement
|
|
36
36
|
for (const statement of dropStatements) {
|
|
37
|
+
// eslint-disable-next-line no-console
|
|
37
38
|
console.debug(`execute DDL: ${statement}`);
|
|
38
39
|
await sql.executeDDL(statement);
|
|
39
40
|
}
|
|
@@ -48,6 +49,7 @@ export async function dropSchemaMigrations(): Promise<TriggerResponse<string>> {
|
|
|
48
49
|
error?.debug?.message ??
|
|
49
50
|
error.message ??
|
|
50
51
|
"Unknown error occurred";
|
|
52
|
+
// eslint-disable-next-line no-console
|
|
51
53
|
console.error(errorMessage);
|
|
52
54
|
return getHttpResponse<string>(500, errorMessage);
|
|
53
55
|
}
|
|
@@ -34,6 +34,7 @@ export async function dropTableSchemaMigrations(): Promise<TriggerResponse<strin
|
|
|
34
34
|
|
|
35
35
|
// Execute each statement
|
|
36
36
|
for (const statement of dropStatements) {
|
|
37
|
+
// eslint-disable-next-line no-console
|
|
37
38
|
console.debug(`execute DDL: ${statement}`);
|
|
38
39
|
await sql.executeDDL(statement);
|
|
39
40
|
}
|
|
@@ -48,6 +49,7 @@ export async function dropTableSchemaMigrations(): Promise<TriggerResponse<strin
|
|
|
48
49
|
error?.debug?.message ??
|
|
49
50
|
error.message ??
|
|
50
51
|
"Unknown error occurred";
|
|
52
|
+
// eslint-disable-next-line no-console
|
|
51
53
|
console.error(errorMessage);
|
|
52
54
|
return getHttpResponse<string>(500, errorMessage);
|
|
53
55
|
}
|
|
@@ -46,6 +46,7 @@ export async function fetchSchemaWebTrigger(): Promise<TriggerResponse<string>>
|
|
|
46
46
|
error?.debug?.message ??
|
|
47
47
|
error.message ??
|
|
48
48
|
"Unknown error occurred";
|
|
49
|
+
// eslint-disable-next-line no-console
|
|
49
50
|
console.error(errorMessage);
|
|
50
51
|
return getHttpResponse<string>(500, errorMessage);
|
|
51
52
|
}
|