forge-sql-orm 2.1.16 → 2.1.18

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.
@@ -0,0 +1,114 @@
1
+ import { AsyncEvent } from "@forge/events";
2
+ import {
3
+ MetadataQueryOptions,
4
+ printDegradationQueries,
5
+ Statistic,
6
+ } from "../utils/metadataContextUtils";
7
+ import { ForgeSqlOperation } from "../core/ForgeSQLQueryBuilder";
8
+
9
+ /**
10
+ * Event payload for async query degradation analysis.
11
+ * Contains query performance statistics and metadata for analysis.
12
+ */
13
+ export type AsyncEventPrintQuery = {
14
+ /** Total database execution time across all queries in milliseconds */
15
+ totalDbExecutionTime: number;
16
+ /** Total response size across all queries in bytes */
17
+ totalResponseSize: number;
18
+ /** Timestamp when the query execution started */
19
+ beginTime: Date;
20
+ /** Query analysis options with all fields set to defaults if not provided */
21
+ options: Required<MetadataQueryOptions>;
22
+ /** Array of query statistics including SQL, parameters, and execution metadata */
23
+ statistics: Statistic[];
24
+ };
25
+
26
+ /**
27
+ * Consumer function for processing async query degradation events.
28
+ *
29
+ * This function is called by the Forge event system when a query degradation event
30
+ * is received from the queue. It processes the event and prints query performance
31
+ * analysis including execution plans for slow queries.
32
+ *
33
+ * The function logs a warning message with job ID, total DB time, query count, and
34
+ * start time to help correlate with the original resolver/service logs.
35
+ *
36
+ * @param forgeSQLORM - The ForgeSQL operation instance for database access
37
+ * @param event - The async event containing query degradation data
38
+ * @returns Promise that resolves when query analysis is complete
39
+ *
40
+ * @example
41
+ * ```yaml
42
+ * # manifest.yml - Configure consumer for async query degradation analysis
43
+ * modules:
44
+ * consumer:
45
+ * - key: print-degradation-queries
46
+ * queue: degradationQueue
47
+ * function: handlerAsyncDegradation
48
+ * function:
49
+ * - key: handlerAsyncDegradation
50
+ * handler: index.handlerAsyncDegradation
51
+ * ```
52
+ *
53
+ * @example
54
+ * ```typescript
55
+ * // index.ts - Handler function that processes async events
56
+ * import { AsyncEvent } from "@forge/events";
57
+ * import { printDegradationQueriesConsumer } from "forge-sql-orm";
58
+ * import { FORGE_SQL_ORM } from "./utils/forgeSqlOrmUtils";
59
+ *
60
+ * export const handlerAsyncDegradation = (event: AsyncEvent) => {
61
+ * return printDegradationQueriesConsumer(FORGE_SQL_ORM, event);
62
+ * };
63
+ * ```
64
+ *
65
+ * @example
66
+ * ```typescript
67
+ * // Using async queue in resolver - Enable async processing
68
+ * resolver.define("fetch", async (req: Request) => {
69
+ * return await FORGE_SQL_ORM.executeWithMetadata(
70
+ * async () => {
71
+ * // ... your queries ...
72
+ * return await SQL_QUERY;
73
+ * },
74
+ * async (totalDbExecutionTime, totalResponseSize, printQueries) => {
75
+ * if (totalDbExecutionTime > 800) {
76
+ * await printQueries(); // Will queue for async processing
77
+ * }
78
+ * },
79
+ * { asyncQueueName: "degradationQueue" } // Enable async processing
80
+ * );
81
+ * });
82
+ * ```
83
+ *
84
+ * @example
85
+ * ```typescript
86
+ * // Log correlation - How to find related logs
87
+ * //
88
+ * // 1. In resolver/service log, you'll see:
89
+ * // [Performance Analysis] Query degradation event queued for async processing | Job ID: abc-123 | ...
90
+ * //
91
+ * // 2. In consumer log (handlerAsyncDegradation), search for the same Job ID:
92
+ * // [Performance Analysis] Processing query degradation event | Job ID: abc-123 | ...
93
+ * //
94
+ * // 3. To find all related logs, search logs for: "Job ID: abc-123"
95
+ * // This will show both the queuing event and the processing event
96
+ * //
97
+ * // Example log flow:
98
+ * // WARN resolver: [Performance Analysis] Query degradation event queued... | Job ID: abc-123
99
+ * // WARN handlerAsyncDegradation: [Performance Analysis] Processing query degradation event | Job ID: abc-123
100
+ * // WARN handlerAsyncDegradation: SQL: SELECT ... | Time: 3514 ms
101
+ * ```
102
+ */
103
+ export async function printDegradationQueriesConsumer(
104
+ forgeSQLORM: ForgeSqlOperation,
105
+ event: AsyncEvent,
106
+ ): Promise<void> {
107
+ const body: AsyncEventPrintQuery = event.body as AsyncEventPrintQuery;
108
+ body.beginTime = new Date(body.beginTime);
109
+ // eslint-disable-next-line no-console
110
+ console.warn(
111
+ `[Performance Analysis] Processing query degradation event | Job ID: ${event.jobId} | Total DB time: ${body.totalDbExecutionTime}ms | Queries: ${body.statistics.length} | Started: ${body.beginTime.toISOString()}`,
112
+ );
113
+ await printDegradationQueries(forgeSQLORM, body);
114
+ }
package/src/index.ts CHANGED
@@ -8,3 +8,4 @@ export * from "./utils/forgeDriver";
8
8
  export * from "./webtriggers";
9
9
  export * from "./lib/drizzle/extensions/additionalActions";
10
10
  export * from "./core/SystemTables";
11
+ export * from "./async/PrintQueryConsumer";
@@ -1,7 +1,7 @@
1
1
  import { forgeDriver } from "./forgeDriver";
2
2
  import { injectSqlHints, SqlHints } from "./sqlHints";
3
3
  import { ForgeSqlOperation } from "../core/ForgeSQLQueryBuilder";
4
- import { printQueriesWithPlan } from "./sqlUtils";
4
+ import { handleErrorsWithPlan } from "./sqlUtils";
5
5
 
6
6
  /**
7
7
  * Error codes and constants for query analysis
@@ -57,22 +57,22 @@ export function createForgeDriverProxy(
57
57
  error?.context?.debug?.errno === QUERY_ERROR_CODES.OUT_OF_MEMORY_ERRNO;
58
58
 
59
59
  if (isTimeoutError || isOutOfMemoryError) {
60
+ // Wait for CLUSTER_STATEMENTS_SUMMARY to be populated with our failed query data
61
+ await new Promise((resolve) => setTimeout(resolve, STATEMENTS_SUMMARY_DELAY_MS));
62
+
63
+ const queryEndTime = Date.now();
64
+ const queryDuration = queryEndTime - queryStartTime;
65
+ let errorType: "OOM" | "TIMEOUT" = "TIMEOUT";
60
66
  if (isTimeoutError) {
61
67
  // eslint-disable-next-line no-console
62
68
  console.error(` TIMEOUT detected - Query exceeded time limit`);
63
69
  } else {
64
70
  // eslint-disable-next-line no-console
65
71
  console.error(`OUT OF MEMORY detected - Query exceeded memory limit`);
72
+ errorType = "OOM";
66
73
  }
67
-
68
- // Wait for CLUSTER_STATEMENTS_SUMMARY to be populated with our failed query data
69
- await new Promise((resolve) => setTimeout(resolve, STATEMENTS_SUMMARY_DELAY_MS));
70
-
71
- const queryEndTime = Date.now();
72
- const queryDuration = queryEndTime - queryStartTime;
73
-
74
74
  // Analyze the failed query using CLUSTER_STATEMENTS_SUMMARY
75
- await printQueriesWithPlan(forgeSqlOperation, queryDuration);
75
+ await handleErrorsWithPlan(forgeSqlOperation, queryDuration, errorType);
76
76
  }
77
77
 
78
78
  // Log SQL error details if requested
@@ -2,12 +2,15 @@ import { AsyncLocalStorage } from "node:async_hooks";
2
2
  import { ForgeSQLMetadata } from "./forgeDriver";
3
3
  import { ForgeSqlOperation } from "../core/ForgeSQLQueryBuilder";
4
4
  import { ExplainAnalyzeRow } from "../core/SystemTables";
5
- import { printQueriesWithPlan } from "./sqlUtils";
5
+ import { printQueriesWithPlan, withTimeout } from "./sqlUtils";
6
6
  import { Parser } from "node-sql-parser";
7
+ import { PushResult, Queue } from "@forge/events";
8
+ import { AsyncEventPrintQuery } from "../async/PrintQueryConsumer";
7
9
 
10
+ const TIMEOUT_ASYNC_EVENT_SENT = 1200;
8
11
  const DEFAULT_WINDOW_SIZE = 15 * 1000;
9
12
 
10
- type Statistic = { query: string; params: unknown[]; metadata: ForgeSQLMetadata };
13
+ export type Statistic = { query: string; params: unknown[]; metadata: ForgeSQLMetadata };
11
14
 
12
15
  export type QueryPlanMode = "TopSlowest" | "SummaryTable";
13
16
 
@@ -17,6 +20,7 @@ export type MetadataQueryOptions = {
17
20
  topQueries?: number;
18
21
  showSlowestPlans?: boolean;
19
22
  normalizeQuery?: boolean;
23
+ asyncQueueName?: string;
20
24
  };
21
25
 
22
26
  export type MetadataQueryContext = {
@@ -42,6 +46,7 @@ function createDefaultOptions(): Required<MetadataQueryOptions> {
42
46
  summaryTableWindowTime: DEFAULT_WINDOW_SIZE,
43
47
  showSlowestPlans: true,
44
48
  normalizeQuery: true,
49
+ asyncQueueName: "",
45
50
  };
46
51
  }
47
52
 
@@ -58,6 +63,7 @@ function mergeOptionsWithDefaults(options?: MetadataQueryOptions): Required<Meta
58
63
  summaryTableWindowTime: options?.summaryTableWindowTime ?? defaults.summaryTableWindowTime,
59
64
  showSlowestPlans: options?.showSlowestPlans ?? defaults.showSlowestPlans,
60
65
  normalizeQuery: options?.normalizeQuery ?? defaults.normalizeQuery,
66
+ asyncQueueName: options?.asyncQueueName ?? defaults.asyncQueueName,
61
67
  };
62
68
  }
63
69
 
@@ -196,16 +202,21 @@ function formatExplainPlan(planRows: ExplainAnalyzeRow[]): string {
196
202
 
197
203
  /**
198
204
  * Prints query plans using summary tables if mode is SummaryTable and within time window.
199
- * @param context - The metadata query context
200
- * @param options - The merged options with defaults
201
- * @returns Promise that resolves when plans are printed
205
+ *
206
+ * Attempts to use CLUSTER_STATEMENTS_SUMMARY table for query analysis if:
207
+ * - Mode is set to "SummaryTable"
208
+ * - Time since query execution start is within the configured window
209
+ *
210
+ * @param context - The async event payload containing query statistics and options
211
+ * @param forgeSQLORM - The ForgeSQL operation instance for database access
212
+ * @returns Promise that resolves to true if summary tables were used, false otherwise
202
213
  */
203
214
  async function printPlansUsingSummaryTables(
204
- context: MetadataQueryContext,
205
- options: Required<MetadataQueryOptions>,
215
+ context: AsyncEventPrintQuery,
216
+ forgeSQLORM: ForgeSqlOperation,
206
217
  ): Promise<boolean> {
207
218
  const timeDiff = Date.now() - context.beginTime.getTime();
208
-
219
+ const options = context.options;
209
220
  if (options.mode !== "SummaryTable") {
210
221
  return false;
211
222
  }
@@ -213,7 +224,7 @@ async function printPlansUsingSummaryTables(
213
224
  if (timeDiff <= options.summaryTableWindowTime) {
214
225
  await new Promise((resolve) => setTimeout(resolve, 200));
215
226
  const summaryTableDiffMs = Date.now() - context.beginTime.getTime();
216
- await printQueriesWithPlan(context.forgeSQLORM, summaryTableDiffMs);
227
+ await printQueriesWithPlan(forgeSQLORM, summaryTableDiffMs);
217
228
  return true;
218
229
  }
219
230
  // eslint-disable-next-line no-console
@@ -222,15 +233,20 @@ async function printPlansUsingSummaryTables(
222
233
  }
223
234
 
224
235
  /**
225
- * Prints query plans for the top slowest queries.
226
- * @param context - The metadata query context
227
- * @param options - The merged options with defaults
228
- * @returns Promise that resolves when plans are printed
236
+ * Prints query plans for the top slowest queries from the statistics.
237
+ *
238
+ * Sorts queries by execution time and prints the top N queries (based on topQueries option).
239
+ * For each query, it can optionally print the execution plan using EXPLAIN ANALYZE.
240
+ *
241
+ * @param context - The async event payload containing query statistics and options
242
+ * @param forgeSQLORM - The ForgeSQL operation instance for database access
243
+ * @returns Promise that resolves when all query plans are printed
229
244
  */
230
245
  async function printTopQueriesPlans(
231
- context: MetadataQueryContext,
232
- options: Required<MetadataQueryOptions>,
246
+ context: AsyncEventPrintQuery,
247
+ forgeSQLORM: ForgeSqlOperation,
233
248
  ): Promise<void> {
249
+ const options = context.options;
234
250
  const topQueries = context.statistics
235
251
  .toSorted((a, b) => b.metadata.dbExecutionTime - a.metadata.dbExecutionTime)
236
252
  .slice(0, options.topQueries);
@@ -240,7 +256,7 @@ async function printTopQueriesPlans(
240
256
  ? normalizeSqlForLogging(query.query)
241
257
  : query.query;
242
258
  if (options.showSlowestPlans) {
243
- const explainAnalyzeRows = await context.forgeSQLORM
259
+ const explainAnalyzeRows = await forgeSQLORM
244
260
  .analyze()
245
261
  .explainAnalyzeRaw(query.query, query.params);
246
262
  const formattedPlan = formatExplainPlan(explainAnalyzeRows);
@@ -257,9 +273,33 @@ async function printTopQueriesPlans(
257
273
 
258
274
  /**
259
275
  * Saves query metadata to the current context and sets up the printQueriesWithPlan function.
276
+ *
277
+ * This function accumulates query statistics in the async context. When printQueriesWithPlan
278
+ * is called, it can either:
279
+ * - Queue the analysis for async processing (if asyncQueueName is provided)
280
+ * - Execute the analysis synchronously (fallback or if asyncQueueName is not set)
281
+ *
282
+ * For async processing, the function sends an event to the specified queue with a timeout.
283
+ * If the event cannot be sent within the timeout, it falls back to synchronous execution.
284
+ *
260
285
  * @param stringQuery - The SQL query string
261
- * @param params - Query parameters
262
- * @param metadata - Query execution metadata
286
+ * @param params - Query parameters used in the query
287
+ * @param metadata - Query execution metadata including execution time and response size
288
+ *
289
+ * @example
290
+ * ```typescript
291
+ * await FORGE_SQL_ORM.executeWithMetadata(
292
+ * async () => {
293
+ * // ... queries ...
294
+ * },
295
+ * async (totalDbExecutionTime, totalResponseSize, printQueries) => {
296
+ * if (totalDbExecutionTime > threshold) {
297
+ * await printQueries(); // Will use async queue if configured
298
+ * }
299
+ * },
300
+ * { asyncQueueName: "degradationQueue" }
301
+ * );
302
+ * ```
263
303
  */
264
304
  export async function saveMetaDataToContext(
265
305
  stringQuery: string,
@@ -285,15 +325,41 @@ export async function saveMetaDataToContext(
285
325
  // Set up printQueriesWithPlan function
286
326
  context.printQueriesWithPlan = async () => {
287
327
  const options = mergeOptionsWithDefaults(context.options);
288
-
289
- // Try to use summary tables first if enabled
290
- const usedSummaryTables = await printPlansUsingSummaryTables(context, options);
291
- if (usedSummaryTables) {
292
- return;
328
+ const param: AsyncEventPrintQuery = {
329
+ statistics: context.statistics,
330
+ totalDbExecutionTime: context.totalDbExecutionTime,
331
+ totalResponseSize: context.totalResponseSize,
332
+ beginTime: context.beginTime,
333
+ options,
334
+ };
335
+ if (options.asyncQueueName) {
336
+ const queue = new Queue({ key: options.asyncQueueName });
337
+ try {
338
+ const eventInfo = await withTimeout<PushResult>(
339
+ queue.push({
340
+ body: param,
341
+ concurrency: {
342
+ key: "orm_" + options.asyncQueueName,
343
+ limit: 2,
344
+ },
345
+ }),
346
+ `Event was not sent within ${TIMEOUT_ASYNC_EVENT_SENT}ms`,
347
+ TIMEOUT_ASYNC_EVENT_SENT,
348
+ );
349
+ // eslint-disable-next-line no-console
350
+ console.warn(
351
+ `[Performance Analysis] Query degradation event queued for async processing | Job ID: ${eventInfo.jobId} | Total DB time: ${context.totalDbExecutionTime}ms | Queries: ${context.statistics.length} | Look for consumer log with jobId: ${eventInfo.jobId}`,
352
+ );
353
+ return;
354
+ } catch (e: any) {
355
+ // eslint-disable-next-line no-console
356
+ console.warn(
357
+ "Async printing failed — falling back to synchronous execution: " + e.message,
358
+ e,
359
+ );
360
+ }
293
361
  }
294
-
295
- // Fall back to printing top queries plans
296
- await printTopQueriesPlans(context, options);
362
+ await printDegradationQueries(context.forgeSQLORM, param);
297
363
  };
298
364
 
299
365
  // Update aggregated metrics
@@ -303,6 +369,34 @@ export async function saveMetaDataToContext(
303
369
  }
304
370
  }
305
371
 
372
+ /**
373
+ * Prints query degradation analysis for the provided event payload.
374
+ *
375
+ * This function processes query degradation events (either from async queue or synchronous call).
376
+ * It first attempts to use summary tables (CLUSTER_STATEMENTS_SUMMARY) if configured and within
377
+ * the time window. Otherwise, it falls back to printing execution plans for the top slowest queries.
378
+ *
379
+ * @param forgeSQLORM - The ForgeSQL operation instance for database access
380
+ * @param params - The async event payload containing query statistics, options, and metadata
381
+ * @returns Promise that resolves when query analysis is complete
382
+ *
383
+ * @see printPlansUsingSummaryTables - For summary table analysis
384
+ * @see printTopQueriesPlans - For top slowest queries analysis
385
+ */
386
+ export async function printDegradationQueries(
387
+ forgeSQLORM: ForgeSqlOperation,
388
+ params: AsyncEventPrintQuery,
389
+ ): Promise<void> {
390
+ // Try to use summary tables first if enabled
391
+ const usedSummaryTables = await printPlansUsingSummaryTables(params, forgeSQLORM);
392
+ if (usedSummaryTables) {
393
+ return;
394
+ }
395
+
396
+ // Fall back to printing top queries plans
397
+ await printTopQueriesPlans(params, forgeSQLORM);
398
+ }
399
+
306
400
  /**
307
401
  * Gets the latest metadata from the current context.
308
402
  * @returns The current metadata context or undefined if not in a context
@@ -2,6 +2,7 @@ import {
2
2
  and,
3
3
  AnyColumn,
4
4
  Column,
5
+ desc,
5
6
  gte,
6
7
  ilike,
7
8
  isNotNull,
@@ -711,6 +712,39 @@ export function nextVal(sequenceName: string): number {
711
712
  return sql.raw(`NEXTVAL(${sequenceName})`) as unknown as number;
712
713
  }
713
714
 
715
+ /**
716
+ * Helper function to build base query for CLUSTER_STATEMENTS_SUMMARY table
717
+ */
718
+ function buildClusterStatementsSummaryQuery(forgeSQLORM: ForgeSqlOperation, timeDiffMs: number) {
719
+ const statementsTable = clusterStatementsSummary;
720
+ return forgeSQLORM
721
+ .getDrizzleQueryBuilder()
722
+ .select({
723
+ digestText: withTidbHint(statementsTable.digestText),
724
+ avgLatency: statementsTable.avgLatency,
725
+ avgMem: statementsTable.avgMem,
726
+ execCount: statementsTable.execCount,
727
+ plan: statementsTable.plan,
728
+ stmtType: statementsTable.stmtType,
729
+ })
730
+ .from(statementsTable)
731
+ .where(
732
+ and(
733
+ isNotNull(statementsTable.digest),
734
+ not(ilike(statementsTable.digestText, "%information_schema%")),
735
+ notInArray(statementsTable.stmtType, ["Use", "Set", "Show", "Commit", "Rollback", "Begin"]),
736
+ gte(
737
+ statementsTable.lastSeen,
738
+ sql`DATE_SUB
739
+ (NOW(), INTERVAL
740
+ ${timeDiffMs * 1000}
741
+ MICROSECOND
742
+ )`,
743
+ ),
744
+ ),
745
+ );
746
+ }
747
+
714
748
  /**
715
749
  * Analyzes and prints query performance data from CLUSTER_STATEMENTS_SUMMARY table.
716
750
  *
@@ -724,7 +758,7 @@ export function nextVal(sequenceName: string): number {
724
758
  *
725
759
  * @param forgeSQLORM - The ForgeSQL operation instance for database access
726
760
  * @param timeDiffMs - Time window in milliseconds to look back for queries (e.g., 1500 for last 1.5 seconds)
727
- * @param timeout - Optional timeout in milliseconds for the query execution (defaults to 1500ms)
761
+ * @param timeout - Optional timeout in milliseconds for the query execution (defaults to 3000ms)
728
762
  *
729
763
  * @example
730
764
  * ```typescript
@@ -743,42 +777,9 @@ export async function printQueriesWithPlan(
743
777
  timeout?: number,
744
778
  ) {
745
779
  try {
746
- const statementsTable = clusterStatementsSummary;
747
780
  const timeoutMs = timeout ?? 3000;
748
781
  const results = await withTimeout(
749
- forgeSQLORM
750
- .getDrizzleQueryBuilder()
751
- .select({
752
- digestText: withTidbHint(statementsTable.digestText),
753
- avgLatency: statementsTable.avgLatency,
754
- avgMem: statementsTable.avgMem,
755
- execCount: statementsTable.execCount,
756
- plan: statementsTable.plan,
757
- stmtType: statementsTable.stmtType,
758
- })
759
- .from(statementsTable)
760
- .where(
761
- and(
762
- isNotNull(statementsTable.digest),
763
- not(ilike(statementsTable.digestText, "%information_schema%")),
764
- notInArray(statementsTable.stmtType, [
765
- "Use",
766
- "Set",
767
- "Show",
768
- "Commit",
769
- "Rollback",
770
- "Begin",
771
- ]),
772
- gte(
773
- statementsTable.lastSeen,
774
- sql`DATE_SUB
775
- (NOW(), INTERVAL
776
- ${timeDiffMs * 1000}
777
- MICROSECOND
778
- )`,
779
- ),
780
- ),
781
- ),
782
+ buildClusterStatementsSummaryQuery(forgeSQLORM, timeDiffMs),
782
783
  `Timeout ${timeoutMs}ms in printQueriesWithPlan - transient timeouts are usually fine; repeated timeouts mean this diagnostic query is consistently slow and should be investigated`,
783
784
  timeoutMs + 200,
784
785
  );
@@ -803,6 +804,44 @@ export async function printQueriesWithPlan(
803
804
  }
804
805
  }
805
806
 
807
+ export async function handleErrorsWithPlan(
808
+ forgeSQLORM: ForgeSqlOperation,
809
+ timeDiffMs: number,
810
+ type: "OOM" | "TIMEOUT",
811
+ ) {
812
+ try {
813
+ const statementsTable = clusterStatementsSummary;
814
+ const timeoutMs = 3000;
815
+ const baseQuery = buildClusterStatementsSummaryQuery(forgeSQLORM, timeDiffMs);
816
+ const orderColumn = type === "OOM" ? statementsTable.avgMem : statementsTable.avgLatency;
817
+ const query = baseQuery.orderBy(desc(orderColumn)).limit(formatLimitOffset(1));
818
+
819
+ const results = await withTimeout(
820
+ query,
821
+ `Timeout ${timeoutMs}ms in handleErrorsWithPlan - transient timeouts are usually fine; repeated timeouts mean this diagnostic query is consistently slow and should be investigated`,
822
+ timeoutMs + 200,
823
+ );
824
+
825
+ for (const result of results) {
826
+ // Average execution time (convert from nanoseconds to milliseconds)
827
+ const avgTimeMs = Number(result.avgLatency) / 1_000_000;
828
+ const avgMemMB = Number(result.avgMem) / 1_000_000;
829
+
830
+ // 1. Query info: SQL, memory, time, executions
831
+ // eslint-disable-next-line no-console
832
+ console.warn(
833
+ `SQL: ${result.digestText} | Memory: ${avgMemMB.toFixed(2)} MB | Time: ${avgTimeMs.toFixed(2)} ms | stmtType: ${result.stmtType} | Executions: ${result.execCount}\n Plan:${result.plan}`,
834
+ );
835
+ }
836
+ } catch (error) {
837
+ // eslint-disable-next-line no-console
838
+ console.debug(
839
+ `Error occurred while retrieving query execution plan: ${error instanceof Error ? error.message : "Unknown error"}. Try again after some time`,
840
+ error,
841
+ );
842
+ }
843
+ }
844
+
806
845
  const SESSION_ALIAS_NAME_ORM = "orm";
807
846
 
808
847
  /**
@@ -60,9 +60,9 @@ async function generateCreateTableStatements(tables: string[]): Promise<string[]
60
60
 
61
61
  for (const table of tables) {
62
62
  const createTableResult = await sql.executeDDL<CreateTableRow>(`SHOW CREATE TABLE "${table}"`);
63
-
64
63
  const createTableStatements = createTableResult.rows
65
64
  .filter((row) => !isSystemTable(row.Table))
65
+ .filter((row) => Boolean(row["Create Table"]))
66
66
  .map((row) => formatCreateTableStatement(row["Create Table"]));
67
67
 
68
68
  statements.push(...createTableStatements);