@bytebase/dbhub 1.2.5 → 1.3.1

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,343 @@
1
+ import {
2
+ hasMutatingKeyword
3
+ } from "./chunk-ABTVMHD6.js";
4
+ import {
5
+ blankCommentsAndStrings
6
+ } from "./chunk-IXC4NQGC.js";
7
+
8
+ // src/utils/sql-row-limiter.ts
9
+ var SQLRowLimiter = class {
10
+ /**
11
+ * Check if a SQL statement is a row-returning query that can benefit from row limiting.
12
+ *
13
+ * Classification runs on the comment-blanked text, so a query introduced by a
14
+ * `--` or `/* ... *\/` comment (a query tag, say) is classified by its real
15
+ * leading keyword. The SQL sent to the server is never rewritten by this
16
+ * check, so an attribution comment survives verbatim.
17
+ *
18
+ * `WITH` leads a CTE whose final statement is normally a SELECT, so it is
19
+ * limitable too, except for a data-modifying CTE
20
+ * (`WITH x AS (DELETE ... RETURNING *) SELECT ...`), where a LIMIT would cap
21
+ * the rows handed back while the write still runs in full. Detection there is
22
+ * the same keyword heuristic the read-only classifier uses, so a false
23
+ * positive only means the statement keeps the old behaviour of not being limited.
24
+ *
25
+ * `dialect` selects the connector's scanner so dialect-specific quoting
26
+ * (PostgreSQL dollar quotes, MySQL backticks, SQL Server brackets) is
27
+ * blanked the same way the read-only classifier blanks it; without it only
28
+ * ANSI syntax is recognized.
29
+ */
30
+ static isSelectQuery(sql, dialect) {
31
+ const blankedSQL = blankCommentsAndStrings(sql, dialect).trim().toLowerCase();
32
+ const firstKeyword = /^[(;\s]*([a-z_]+)/.exec(blankedSQL)?.[1] ?? "";
33
+ if (firstKeyword === "select") {
34
+ return true;
35
+ }
36
+ return firstKeyword === "with" && !hasMutatingKeyword(blankedSQL, dialect);
37
+ }
38
+ /**
39
+ * Check if a SQL statement has a LIMIT clause of its own (not one nested in
40
+ * a CTE body or subquery).
41
+ */
42
+ static hasLimitClause(sql, dialect) {
43
+ return this.findTopLevelLimit(sql, dialect) !== null;
44
+ }
45
+ /**
46
+ * Check if a SQL statement has a TOP clause of its own (SQL Server).
47
+ */
48
+ static hasTopClause(sql) {
49
+ return this.findTopLevelTop(sql) !== null;
50
+ }
51
+ /**
52
+ * Extract the statement's own LIMIT value. Null when it has no LIMIT of its
53
+ * own, or when that LIMIT is a parameter placeholder rather than a literal
54
+ * (see hasParameterizedLimit).
55
+ */
56
+ static extractLimitValue(sql, dialect) {
57
+ return this.findTopLevelLimit(sql, dialect)?.value ?? null;
58
+ }
59
+ /**
60
+ * Extract the statement's own TOP value (SQL Server), or null when it has none.
61
+ */
62
+ static extractTopValue(sql) {
63
+ return this.findTopLevelTop(sql)?.value ?? null;
64
+ }
65
+ /**
66
+ * Check if the statement's own LIMIT clause uses a parameter placeholder
67
+ * ($1, ?, @p1) instead of a literal number.
68
+ */
69
+ static hasParameterizedLimit(sql, dialect) {
70
+ const limit = this.findTopLevelLimit(sql, dialect);
71
+ return limit !== null && limit.value === null;
72
+ }
73
+ /**
74
+ * Add or tighten the LIMIT clause of a SQL statement
75
+ */
76
+ static applyLimitToQuery(sql, maxRows, dialect) {
77
+ const limit = this.findTopLevelLimit(sql, dialect);
78
+ if (limit !== null && limit.value !== null) {
79
+ const effectiveLimit = Math.min(limit.value, maxRows);
80
+ return `${sql.slice(0, limit.index)}LIMIT ${effectiveLimit}${sql.slice(limit.index + limit.length)}`;
81
+ }
82
+ const { sql: sqlWithoutSemicolon, semicolon } = trimSemicolon(sql);
83
+ return `${sqlWithoutSemicolon}
84
+ LIMIT ${maxRows}${semicolon}`;
85
+ }
86
+ /**
87
+ * Scan blanked (comment/string-free, length-preserving) SQL for parenthesis
88
+ * depth, invoking onMatch for every regex hit at depth 0 (i.e. not nested
89
+ * inside a subquery, function call, or window OVER clause).
90
+ */
91
+ static scanTopLevel(blankedSQL, regex, onMatch) {
92
+ let depth = 0;
93
+ let m;
94
+ while ((m = regex.exec(blankedSQL)) !== null) {
95
+ const token = m[0];
96
+ if (token === "(") {
97
+ depth++;
98
+ } else if (token === ")") {
99
+ depth--;
100
+ } else if (depth === 0) {
101
+ onMatch(m);
102
+ }
103
+ }
104
+ }
105
+ /**
106
+ * The first or last regex match at parenthesis depth 0, i.e. one belonging
107
+ * to the statement itself rather than to a nested query. Match indices refer
108
+ * to positions in the original `sql`, since blankCommentsAndStrings
109
+ * preserves length. The regex must offer `\(` and `\)` alternatives so depth
110
+ * can be tracked, and must carry the `g` flag.
111
+ */
112
+ static findTopLevelMatch(sql, regex, pick, dialect) {
113
+ let found = null;
114
+ this.scanTopLevel(blankCommentsAndStrings(sql, dialect), regex, (m) => {
115
+ if (pick === "last" || found === null) {
116
+ found = m;
117
+ }
118
+ });
119
+ return found;
120
+ }
121
+ /** The statement's own LIMIT clause, literal or parameterized ($1, ?, @p1). */
122
+ static findTopLevelLimit(sql, dialect) {
123
+ const match = this.findTopLevelMatch(
124
+ sql,
125
+ /\(|\)|\blimit\s+(?:(\d+)|\$\d+|\?|@p\d+)/gi,
126
+ "last",
127
+ dialect
128
+ );
129
+ if (match === null) {
130
+ return null;
131
+ }
132
+ return {
133
+ index: match.index,
134
+ length: match[0].length,
135
+ value: match[1] !== void 0 ? parseInt(match[1], 10) : null
136
+ };
137
+ }
138
+ /** The statement's own `SELECT TOP n` clause (SQL Server). */
139
+ static findTopLevelTop(sql) {
140
+ const match = this.findTopLevelMatch(sql, /\(|\)|\bselect\s+top\s+(\d+)/gi, "first", "sqlserver");
141
+ if (match === null) {
142
+ return null;
143
+ }
144
+ return { index: match.index, length: match[0].length, value: parseInt(match[1], 10) };
145
+ }
146
+ /**
147
+ * The statement's own SELECT keyword (SQL Server). For a CTE this is the
148
+ * final SELECT, not the one inside a CTE body.
149
+ */
150
+ static findTopLevelSelect(sql) {
151
+ return this.findTopLevelMatch(sql, /\(|\)|\bselect\b/gi, "first", "sqlserver");
152
+ }
153
+ /**
154
+ * Check if a SQL statement combines multiple SELECTs with a set operator
155
+ * (UNION [ALL], INTERSECT, EXCEPT) at the top level — i.e. not nested
156
+ * inside a subquery already wrapped in parentheses.
157
+ */
158
+ static hasSetOperator(sql) {
159
+ return this.findTopLevelMatch(sql, /\(|\)|\bunion\b|\bintersect\b|\bexcept\b/gi, "first", "sqlserver") !== null;
160
+ }
161
+ /**
162
+ * Find the start index of a top-level trailing ORDER BY clause (not one
163
+ * nested inside a subquery or a window function's OVER (...) clause).
164
+ * Returns -1 if none exists.
165
+ */
166
+ static findTopLevelOrderByIndex(sql) {
167
+ return this.findTopLevelMatch(sql, /\(|\)|\border\s+by\b/gi, "last", "sqlserver")?.index ?? -1;
168
+ }
169
+ /**
170
+ * Add or modify TOP clause in a SQL statement (SQL Server)
171
+ */
172
+ static applyTopToQuery(sql, maxRows) {
173
+ if (this.hasSetOperator(sql)) {
174
+ const { sql: sqlWithoutSemicolon, semicolon } = trimSemicolon(sql);
175
+ const cteIndex = this.findTopLevelSelect(sqlWithoutSemicolon)?.index ?? 0;
176
+ const ctePrefix = sqlWithoutSemicolon.slice(0, cteIndex);
177
+ const body = sqlWithoutSemicolon.slice(cteIndex);
178
+ const orderByIndex = this.findTopLevelOrderByIndex(body);
179
+ if (orderByIndex !== -1) {
180
+ const innerSql = body.slice(0, orderByIndex).trimEnd();
181
+ const orderByClause = body.slice(orderByIndex).trim();
182
+ return `${ctePrefix}SELECT TOP ${maxRows} * FROM (${innerSql}
183
+ ) AS subq ${orderByClause}${semicolon}`;
184
+ }
185
+ return `${ctePrefix}SELECT TOP ${maxRows} * FROM (${body}
186
+ ) AS subq${semicolon}`;
187
+ }
188
+ const existingTop = this.findTopLevelTop(sql);
189
+ if (existingTop !== null) {
190
+ const effectiveTop = Math.min(existingTop.value, maxRows);
191
+ return `${sql.slice(0, existingTop.index)}SELECT TOP ${effectiveTop}${sql.slice(existingTop.index + existingTop.length)}`;
192
+ }
193
+ const selectMatch = this.findTopLevelSelect(sql);
194
+ if (selectMatch === null) {
195
+ return sql;
196
+ }
197
+ return `${sql.slice(0, selectMatch.index)}SELECT TOP ${maxRows}${sql.slice(selectMatch.index + selectMatch[0].length)}`;
198
+ }
199
+ /**
200
+ * Apply maxRows limit to a row-returning query only
201
+ *
202
+ * This method is used by PostgreSQL, MySQL, MariaDB, and SQLite connectors which all support
203
+ * the LIMIT clause syntax. SQL Server uses applyMaxRowsForSQLServer() instead with TOP syntax.
204
+ *
205
+ * For parameterized LIMIT clauses (e.g., LIMIT $1 or LIMIT ?), we wrap the query in a subquery
206
+ * to enforce max_rows as a hard cap, since the parameter value is not known until runtime.
207
+ *
208
+ * `dialect` selects the connector's comment/string scanner (see isSelectQuery).
209
+ */
210
+ static applyMaxRows(sql, maxRows, dialect) {
211
+ if (!maxRows || !this.isSelectQuery(sql, dialect)) {
212
+ return sql;
213
+ }
214
+ if (this.hasParameterizedLimit(sql, dialect)) {
215
+ const { sql: sqlWithoutSemicolon, semicolon } = trimSemicolon(sql);
216
+ return `SELECT * FROM (${sqlWithoutSemicolon}
217
+ ) AS subq LIMIT ${maxRows}${semicolon}`;
218
+ }
219
+ return this.applyLimitToQuery(sql, maxRows, dialect);
220
+ }
221
+ /**
222
+ * Apply maxRows limit to a row-returning query using SQL Server TOP syntax
223
+ */
224
+ static applyMaxRowsForSQLServer(sql, maxRows) {
225
+ if (!maxRows || !this.isSelectQuery(sql, "sqlserver")) {
226
+ return sql;
227
+ }
228
+ return this.applyTopToQuery(sql, maxRows);
229
+ }
230
+ /**
231
+ * Apply maxRows to a row-returning query using Oracle's row-limiting clause.
232
+ *
233
+ * The statement is always wrapped in an inline view and capped from the
234
+ * outside: `SELECT * FROM (<sql>) FETCH FIRST n ROWS ONLY`. Wrapping rather
235
+ * than splicing keeps every shape correct with one rule - a set operation
236
+ * (UNION & co.) is capped as a whole, a trailing ORDER BY stays inside the
237
+ * view where Oracle honours it, and a query that already has its own
238
+ * `FETCH FIRST` / `ROWNUM` cap simply returns the smaller of the two. Oracle
239
+ * also accepts a leading CTE (subquery factoring) inside an inline view, so
240
+ * `WITH ... SELECT` needs no special casing.
241
+ */
242
+ static applyMaxRowsForOracle(sql, maxRows) {
243
+ if (!maxRows || !this.isSelectQuery(sql, "oracle") || this.hasOracleForUpdate(sql)) {
244
+ return sql;
245
+ }
246
+ const { sql: sqlWithoutSemicolon } = trimSemicolon(sql);
247
+ return `SELECT * FROM (${sqlWithoutSemicolon}
248
+ ) FETCH FIRST ${maxRows} ROWS ONLY`;
249
+ }
250
+ /**
251
+ * A statement-level `FOR UPDATE` (locking read). Oracle allows neither
252
+ * `FOR UPDATE` inside an inline view nor a row-limiting clause alongside
253
+ * it, so such a statement cannot be capped and is left as written.
254
+ */
255
+ static hasOracleForUpdate(sql) {
256
+ return this.findTopLevelMatch(sql, /\(|\)|\bfor\s+update\b/gi, "first", "oracle") !== null;
257
+ }
258
+ /**
259
+ * Oracle variant of applyMaxRowsWithTruncationProbe. Because the cap always
260
+ * wraps (see applyMaxRowsForOracle), the probe is applied to every
261
+ * row-returning statement; a query whose own cap is tighter than maxRows
262
+ * returns fewer than maxRows + 1 rows and is therefore never flagged.
263
+ * A `FOR UPDATE` statement is never capped (see hasOracleForUpdate).
264
+ */
265
+ static applyMaxRowsForOracleWithTruncationProbe(sql, maxRows) {
266
+ if (!maxRows || !this.isSelectQuery(sql, "oracle") || this.hasOracleForUpdate(sql)) {
267
+ return { sql, probeApplied: false };
268
+ }
269
+ return { sql: this.applyMaxRowsForOracle(sql, maxRows + 1), probeApplied: true };
270
+ }
271
+ /**
272
+ * Like applyMaxRows, but requests one probe row (maxRows + 1) whenever the
273
+ * cap is the binding constraint, so callers can detect truncation exactly
274
+ * instead of guessing from `rowCount === maxRows` (which a table with
275
+ * exactly maxRows rows also produces).
276
+ *
277
+ * `probeApplied` is false when the cap cannot fire: no maxRows, not a
278
+ * SELECT, or the query's own literal LIMIT is already within the cap (the
279
+ * user asked for fewer rows than the cap — that is their limit, not
280
+ * truncation).
281
+ */
282
+ static applyMaxRowsWithTruncationProbe(sql, maxRows, dialect) {
283
+ if (!maxRows || !this.isSelectQuery(sql, dialect)) {
284
+ return { sql, probeApplied: false };
285
+ }
286
+ if (!this.hasParameterizedLimit(sql, dialect)) {
287
+ const existingLimit = this.extractLimitValue(sql, dialect);
288
+ if (existingLimit !== null && existingLimit <= maxRows) {
289
+ return { sql, probeApplied: false };
290
+ }
291
+ }
292
+ return { sql: this.applyMaxRows(sql, maxRows + 1, dialect), probeApplied: true };
293
+ }
294
+ /**
295
+ * SQL Server variant of applyMaxRowsWithTruncationProbe, using TOP syntax.
296
+ * A set-operator statement is always wrapped (any TOP on an individual
297
+ * branch caps only that branch, not the combined output), so the probe
298
+ * applies there regardless of branch-level TOPs.
299
+ */
300
+ static applyMaxRowsForSQLServerWithTruncationProbe(sql, maxRows) {
301
+ if (!maxRows || !this.isSelectQuery(sql, "sqlserver")) {
302
+ return { sql, probeApplied: false };
303
+ }
304
+ if (!this.hasSetOperator(sql)) {
305
+ const existingTop = this.extractTopValue(sql);
306
+ if (existingTop !== null && existingTop <= maxRows) {
307
+ return { sql, probeApplied: false };
308
+ }
309
+ }
310
+ return { sql: this.applyMaxRowsForSQLServer(sql, maxRows + 1), probeApplied: true };
311
+ }
312
+ /**
313
+ * Post-process a result set produced by a truncation-probe rewrite: when
314
+ * the probe row came back (more than maxRows rows), drop it, clamp the
315
+ * count to maxRows, and mark the set truncated so consumers can tell a
316
+ * capped result from a complete one.
317
+ */
318
+ static flagTruncation(resultSet, maxRows, probeApplied) {
319
+ if (!probeApplied || !maxRows || resultSet.rows.length <= maxRows) {
320
+ return;
321
+ }
322
+ resultSet.rows = resultSet.rows.slice(0, maxRows);
323
+ resultSet.rowCount = maxRows;
324
+ resultSet.truncated = true;
325
+ }
326
+ };
327
+ function trimSemicolon(sql) {
328
+ const trimmed = sql.trim();
329
+ return trimmed.endsWith(";") ? { sql: trimmed.slice(0, -1), semicolon: ";" } : { sql: trimmed, semicolon: "" };
330
+ }
331
+
332
+ // src/utils/resource-cleanup.ts
333
+ async function closeQuietly(close) {
334
+ try {
335
+ await close();
336
+ } catch {
337
+ }
338
+ }
339
+
340
+ export {
341
+ SQLRowLimiter,
342
+ closeQuietly
343
+ };
package/dist/index.js CHANGED
@@ -18,20 +18,20 @@ import {
18
18
  resolveSourceConfigs,
19
19
  resolveTomlConfigPath,
20
20
  resolveTransport
21
- } from "./chunk-VDA5BVNE.js";
21
+ } from "./chunk-5FL4XMWG.js";
22
+ import {
23
+ loadConnectors
24
+ } from "./chunk-WVVMH6FJ.js";
25
+ import {
26
+ quoteQualifiedIdentifier
27
+ } from "./chunk-X2EC47W4.js";
22
28
  import {
23
29
  allowedKeywords,
24
30
  getFirstKeyword,
25
31
  hasEscapeHatchFunction,
26
32
  isReadOnlySQL,
27
33
  sqlServerDynamicSqlPattern
28
- } from "./chunk-IQRBLAB4.js";
29
- import {
30
- loadConnectors
31
- } from "./chunk-WVVMH6FJ.js";
32
- import {
33
- quoteQualifiedIdentifier
34
- } from "./chunk-JFWX35TB.js";
34
+ } from "./chunk-ABTVMHD6.js";
35
35
  import {
36
36
  ConnectorRegistry,
37
37
  getDatabaseTypeFromDSN,
@@ -39,7 +39,7 @@ import {
39
39
  parseConnectionInfoFromDSN,
40
40
  splitSQLStatements,
41
41
  stripCommentsAndStrings
42
- } from "./chunk-MF6LSVWK.js";
42
+ } from "./chunk-IXC4NQGC.js";
43
43
 
44
44
  // src/server.ts
45
45
  import { McpServer, createMcpHandler } from "@modelcontextprotocol/server";
@@ -1913,7 +1913,7 @@ See documentation for more details on configuring database connections.
1913
1913
  const sources = sourceConfigsData.sources;
1914
1914
  console.error(`Configuration source: ${sourceConfigsData.source}`);
1915
1915
  await connectorManager.connectWithSources(sources);
1916
- const { initializeToolRegistry: initializeToolRegistry2 } = await import("./registry-UF3N2H4G.js");
1916
+ const { initializeToolRegistry: initializeToolRegistry2 } = await import("./registry-ZTHDPLKP.js");
1917
1917
  initializeToolRegistry2({
1918
1918
  sources: sourceConfigsData.sources,
1919
1919
  tools: sourceConfigsData.tools
@@ -2068,11 +2068,12 @@ See documentation for more details on configuring database connections.
2068
2068
 
2069
2069
  // src/index.ts
2070
2070
  var connectorModules = [
2071
- { load: () => import("./postgres-M3W3Y6F5.js"), name: "PostgreSQL", driver: "pg" },
2072
- { load: () => import("./sqlserver-WNVSBFJ3.js"), name: "SQL Server", driver: "mssql" },
2073
- { load: () => import("./sqlite-ASYMU5BI.js"), name: "SQLite", driver: "node:sqlite" },
2074
- { load: () => import("./mysql-GXDUNLJ6.js"), name: "MySQL", driver: "mysql2" },
2075
- { load: () => import("./mariadb-EVJ5HLYH.js"), name: "MariaDB", driver: "mariadb" }
2071
+ { load: () => import("./postgres-6XJ7V4J4.js"), name: "PostgreSQL", driver: "pg" },
2072
+ { load: () => import("./sqlserver-AJUK6L55.js"), name: "SQL Server", driver: "mssql" },
2073
+ { load: () => import("./sqlite-AO2BKIC6.js"), name: "SQLite", driver: "node:sqlite" },
2074
+ { load: () => import("./mysql-XXIHWQGR.js"), name: "MySQL", driver: "mysql2" },
2075
+ { load: () => import("./mariadb-L7Z55ROY.js"), name: "MariaDB", driver: "mariadb" },
2076
+ { load: () => import("./oracle-OUKBU52X.js"), name: "Oracle", driver: "oracledb" }
2076
2077
  ];
2077
2078
  loadConnectors(connectorModules).then(() => main()).catch((error) => {
2078
2079
  console.error("Fatal error:", error);
@@ -5,21 +5,22 @@ import {
5
5
  parseQueryResultSets,
6
6
  requireDatabaseInDSN,
7
7
  withReadOnlyTransaction
8
- } from "./chunk-A4TSDCPY.js";
8
+ } from "./chunk-6SZKW6GD.js";
9
9
  import "./chunk-FU2ZJE4E.js";
10
10
  import {
11
11
  quoteIdentifier
12
- } from "./chunk-JFWX35TB.js";
12
+ } from "./chunk-X2EC47W4.js";
13
13
  import {
14
14
  SQLRowLimiter,
15
15
  closeQuietly
16
- } from "./chunk-6QETXS44.js";
16
+ } from "./chunk-YIGJHEU6.js";
17
+ import "./chunk-ABTVMHD6.js";
17
18
  import {
18
19
  ConnectorRegistry,
19
20
  SafeURL,
20
21
  obfuscateDSNPassword,
21
22
  splitSQLStatements
22
- } from "./chunk-MF6LSVWK.js";
23
+ } from "./chunk-IXC4NQGC.js";
23
24
 
24
25
  // src/connectors/mariadb/index.ts
25
26
  import * as mariadb from "mariadb";
@@ -515,7 +516,7 @@ var MariaDBConnector = class _MariaDBConnector {
515
516
  let probes = [];
516
517
  if (options.maxRows) {
517
518
  const rewrites = statements.map(
518
- (statement) => SQLRowLimiter.applyMaxRowsWithTruncationProbe(statement, options.maxRows)
519
+ (statement) => SQLRowLimiter.applyMaxRowsWithTruncationProbe(statement, options.maxRows, "mariadb")
519
520
  );
520
521
  probes = rewrites.map((rewrite) => rewrite.probeApplied);
521
522
  processedSQL = rewrites.map((rewrite) => rewrite.sql).join("; ");
@@ -6,21 +6,22 @@ import {
6
6
  parseQueryResultSets,
7
7
  requireDatabaseInDSN,
8
8
  withReadOnlyTransaction
9
- } from "./chunk-A4TSDCPY.js";
9
+ } from "./chunk-6SZKW6GD.js";
10
10
  import "./chunk-FU2ZJE4E.js";
11
11
  import {
12
12
  quoteIdentifier
13
- } from "./chunk-JFWX35TB.js";
13
+ } from "./chunk-X2EC47W4.js";
14
14
  import {
15
15
  SQLRowLimiter,
16
16
  closeQuietly
17
- } from "./chunk-6QETXS44.js";
17
+ } from "./chunk-YIGJHEU6.js";
18
+ import "./chunk-ABTVMHD6.js";
18
19
  import {
19
20
  ConnectorRegistry,
20
21
  SafeURL,
21
22
  obfuscateDSNPassword,
22
23
  splitSQLStatements
23
- } from "./chunk-MF6LSVWK.js";
24
+ } from "./chunk-IXC4NQGC.js";
24
25
 
25
26
  // src/connectors/mysql/index.ts
26
27
  import mysql from "mysql2/promise";
@@ -520,7 +521,7 @@ var _MySQLConnector = class _MySQLConnector {
520
521
  let probes = [];
521
522
  if (options.maxRows) {
522
523
  const rewrites = statements.map(
523
- (statement) => SQLRowLimiter.applyMaxRowsWithTruncationProbe(statement, options.maxRows)
524
+ (statement) => SQLRowLimiter.applyMaxRowsWithTruncationProbe(statement, options.maxRows, "mysql")
524
525
  );
525
526
  probes = rewrites.map((rewrite) => rewrite.probeApplied);
526
527
  processedSQL = rewrites.map((rewrite) => rewrite.sql).join("; ");