@bytebase/dbhub 0.22.6 → 0.24.0

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/dist/index.js CHANGED
@@ -15,7 +15,11 @@ import {
15
15
  resolveSourceConfigs,
16
16
  resolveTomlConfigPath,
17
17
  resolveTransport
18
- } from "./chunk-WPXPKRDU.js";
18
+ } from "./chunk-MQR6VX5F.js";
19
+ import {
20
+ allowedKeywords,
21
+ isReadOnlySQL
22
+ } from "./chunk-JO7YQPBQ.js";
19
23
  import {
20
24
  loadConnectors
21
25
  } from "./chunk-WVVMH6FJ.js";
@@ -27,8 +31,7 @@ import {
27
31
  getDatabaseTypeFromDSN,
28
32
  getDefaultPortForType,
29
33
  parseConnectionInfoFromDSN,
30
- splitSQLStatements,
31
- stripCommentsAndStrings
34
+ splitSQLStatements
32
35
  } from "./chunk-JEZZN2YZ.js";
33
36
 
34
37
  // src/server.ts
@@ -95,98 +98,6 @@ function createToolSuccessResponse(data, meta = {}) {
95
98
  };
96
99
  }
97
100
 
98
- // src/utils/allowed-keywords.ts
99
- var allowedKeywords = {
100
- postgres: ["select", "with", "explain", "show"],
101
- mysql: ["select", "with", "explain", "show", "describe", "desc"],
102
- mariadb: ["select", "with", "explain", "show", "describe", "desc"],
103
- sqlite: ["select", "with", "explain", "pragma"],
104
- // SQL Server has no native EXPLAIN statement; the connector translates a
105
- // leading `EXPLAIN` into a SET SHOWPLAN_XML request (see SQLServerConnector).
106
- sqlserver: ["select", "with", "explain"]
107
- };
108
- var mutatingKeywords = [
109
- "insert",
110
- "update",
111
- "delete",
112
- "drop",
113
- "alter",
114
- "create",
115
- "truncate",
116
- "merge",
117
- "grant",
118
- "revoke",
119
- "rename"
120
- ];
121
- var mutatingPattern = new RegExp(
122
- `\\b(?:${mutatingKeywords.join("|")})\\b`,
123
- "i"
124
- );
125
- var mutatingPatternWithReplace = new RegExp(
126
- `\\b(?:${mutatingKeywords.join("|")}|replace\\s+(?:(?:low_priority|delayed)\\s+)?into)\\b`,
127
- "i"
128
- );
129
- var mutatingPatterns = {
130
- postgres: mutatingPattern,
131
- mysql: mutatingPatternWithReplace,
132
- mariadb: mutatingPatternWithReplace,
133
- sqlite: mutatingPatternWithReplace,
134
- sqlserver: mutatingPattern
135
- };
136
- var selectIntoPattern = /\bselect\b[\s\S]+\binto\b/i;
137
- var sqliteReadOnlyArgPragmas = /* @__PURE__ */ new Set([
138
- "table_info",
139
- "index_info",
140
- "index_list",
141
- "foreign_key_list"
142
- ]);
143
- var sqlitePragmaParenPattern = /^pragma\s+(?:[a-z0-9_]+\.)?([a-z0-9_]+)\s*\(/;
144
- var explainAnalyzePattern = /^explain\s+(?:\([^)]*\banalyze\b(?!\s*(?:=\s*)?(?:false|off|0)\b)[^)]*\)|\banalyze\b(?!\s*(?:=\s*)?(?:false|off|0)\b)(?:\s+verbose\b)?)/i;
145
- function isReadOnlySQL(sql, connectorType) {
146
- return checkReadOnly(
147
- stripCommentsAndStrings(sql, connectorType).trim().toLowerCase(),
148
- connectorType
149
- );
150
- }
151
- function checkReadOnly(cleanedSQL, connectorType) {
152
- if (!cleanedSQL) {
153
- return false;
154
- }
155
- const firstWord = cleanedSQL.match(/\S+/)?.[0] ?? "";
156
- const keywordList = allowedKeywords[connectorType] || [];
157
- if (!keywordList.includes(firstWord)) {
158
- return false;
159
- }
160
- if (firstWord === "with") {
161
- const pattern = mutatingPatterns[connectorType] ?? mutatingPattern;
162
- if (pattern.test(cleanedSQL)) {
163
- return false;
164
- }
165
- }
166
- if (firstWord === "pragma" && connectorType === "sqlite") {
167
- if (cleanedSQL.includes("=")) {
168
- return false;
169
- }
170
- const parenMatch = cleanedSQL.match(sqlitePragmaParenPattern);
171
- if (parenMatch && !sqliteReadOnlyArgPragmas.has(parenMatch[1])) {
172
- return false;
173
- }
174
- }
175
- if ((firstWord === "select" || firstWord === "with") && selectIntoPattern.test(cleanedSQL)) {
176
- return false;
177
- }
178
- if (firstWord === "explain") {
179
- const m = explainAnalyzePattern.exec(cleanedSQL);
180
- if (m) {
181
- const afterExplain = cleanedSQL.slice(m[0].length).trim();
182
- if (afterExplain && !checkReadOnly(afterExplain, connectorType)) {
183
- return false;
184
- }
185
- }
186
- }
187
- return true;
188
- }
189
-
190
101
  // src/requests/store.ts
191
102
  var RequestStore = class {
192
103
  constructor() {
@@ -373,7 +284,7 @@ async function getTableRowCount(connector, tableName, schemaName) {
373
284
  }
374
285
  const qualifiedTable = quoteQualifiedIdentifier(tableName, schemaName, connector.id);
375
286
  const countQuery = `SELECT COUNT(*) as count FROM ${qualifiedTable}`;
376
- const result = await connector.executeSQL(countQuery, { maxRows: 1 });
287
+ const result = await connector.executeSQL(countQuery, { maxRows: 1, readonly: true });
377
288
  if (result.rows && result.rows.length > 0) {
378
289
  return Number(result.rows[0].count || result.rows[0].COUNT || 0);
379
290
  }
@@ -506,7 +417,7 @@ async function searchViews(connector, pattern, schemaFilter, detailLevel, limit)
506
417
  if (schemaFilter) {
507
418
  schemasToSearch = [schemaFilter];
508
419
  } else {
509
- schemasToSearch = await connector.getSchemas();
420
+ schemasToSearch = await resolveDefaultSchemas(connector);
510
421
  }
511
422
  for (const schemaName of schemasToSearch) {
512
423
  if (results.length >= limit) break;
@@ -1672,7 +1583,7 @@ See documentation for more details on configuring database connections.
1672
1583
  const sources = sourceConfigsData.sources;
1673
1584
  console.error(`Configuration source: ${sourceConfigsData.source}`);
1674
1585
  await connectorManager.connectWithSources(sources);
1675
- const { initializeToolRegistry: initializeToolRegistry2 } = await import("./registry-IPRZ55YC.js");
1586
+ const { initializeToolRegistry: initializeToolRegistry2 } = await import("./registry-ZU6ZFNQ2.js");
1676
1587
  initializeToolRegistry2({
1677
1588
  sources: sourceConfigsData.sources,
1678
1589
  tools: sourceConfigsData.tools
@@ -1826,11 +1737,11 @@ See documentation for more details on configuring database connections.
1826
1737
 
1827
1738
  // src/index.ts
1828
1739
  var connectorModules = [
1829
- { load: () => import("./postgres-BKBCGDZS.js"), name: "PostgreSQL", driver: "pg" },
1830
- { load: () => import("./sqlserver-47APXMSF.js"), name: "SQL Server", driver: "mssql" },
1831
- { load: () => import("./sqlite-CQ4LCBB6.js"), name: "SQLite", driver: "node:sqlite" },
1832
- { load: () => import("./mysql-EK6TCER5.js"), name: "MySQL", driver: "mysql2" },
1833
- { load: () => import("./mariadb-JPEKVFEW.js"), name: "MariaDB", driver: "mariadb" }
1740
+ { load: () => import("./postgres-ZQFXCBYA.js"), name: "PostgreSQL", driver: "pg" },
1741
+ { load: () => import("./sqlserver-LHWZ7WOP.js"), name: "SQL Server", driver: "mssql" },
1742
+ { load: () => import("./sqlite-JH5CNV5E.js"), name: "SQLite", driver: "node:sqlite" },
1743
+ { load: () => import("./mysql-MHIZK2UY.js"), name: "MySQL", driver: "mysql2" },
1744
+ { load: () => import("./mariadb-VZMU7LYA.js"), name: "MariaDB", driver: "mariadb" }
1834
1745
  ];
1835
1746
  loadConnectors(connectorModules).then(() => main()).catch((error) => {
1836
1747
  console.error("Fatal error:", error);
@@ -1,13 +1,18 @@
1
1
  import {
2
+ MissingDatabaseError,
2
3
  extractAffectedRows,
3
- parseQueryResults
4
- } from "./chunk-RTB262PR.js";
4
+ isTiDBVersion,
5
+ parseQueryResults,
6
+ requireDatabaseInDSN,
7
+ withReadOnlyTransaction
8
+ } from "./chunk-DUCNPMNW.js";
5
9
  import {
6
10
  quoteIdentifier
7
11
  } from "./chunk-JFWX35TB.js";
8
12
  import {
9
- SQLRowLimiter
10
- } from "./chunk-BXXHWEQS.js";
13
+ SQLRowLimiter,
14
+ closeQuietly
15
+ } from "./chunk-D2BIPC7V.js";
11
16
  import {
12
17
  ConnectorRegistry,
13
18
  SafeURL,
@@ -32,11 +37,12 @@ Expected: ${expectedFormat}`
32
37
  }
33
38
  try {
34
39
  const url = new SafeURL(dsn);
40
+ const database = url.pathname ? url.pathname.substring(1) : "";
41
+ requireDatabaseInDSN(database, dsn, "MariaDB");
35
42
  const connectionConfig = {
36
43
  host: url.hostname,
37
44
  port: url.port ? parseInt(url.port) : 3306,
38
- database: url.pathname ? url.pathname.substring(1) : "",
39
- // Remove leading '/' if exists
45
+ database,
40
46
  user: url.username,
41
47
  password: url.password,
42
48
  multipleStatements: true,
@@ -50,7 +56,14 @@ Expected: ${expectedFormat}`
50
56
  // Controls how the driver interprets DATETIME values ("Z", "local", or "±HH:MM").
51
57
  ...config?.timezone !== void 0 && {
52
58
  timezone: config.timezone
53
- }
59
+ },
60
+ // Connection character set (e.g. "utf8mb4") / collation (e.g.
61
+ // "utf8mb4_general_ci"). The mariadb driver exposes both as distinct
62
+ // options, but a configured collation is authoritative (it implies its
63
+ // character set) and the driver ignores `collation` when `charset` is also
64
+ // passed. So forward the collation when present, otherwise the charset
65
+ // (which uses that character set's default collation).
66
+ ...config?.collation !== void 0 ? { collation: config.collation } : config?.charset !== void 0 ? { charset: config.charset } : {}
54
67
  };
55
68
  url.forEachSearchParam((value, key) => {
56
69
  if (key === "sslmode") {
@@ -70,6 +83,9 @@ Expected: ${expectedFormat}`
70
83
  }
71
84
  return connectionConfig;
72
85
  } catch (error) {
86
+ if (error instanceof MissingDatabaseError) {
87
+ throw error;
88
+ }
73
89
  throw new Error(
74
90
  `Failed to parse MariaDB DSN: ${error instanceof Error ? error.message : String(error)}`
75
91
  );
@@ -94,6 +110,9 @@ var MariaDBConnector = class _MariaDBConnector {
94
110
  this.pool = null;
95
111
  // Source ID is set by ConnectorManager after cloning
96
112
  this.sourceId = "default";
113
+ // TiDB speaks the MySQL protocol but rejects `START TRANSACTION READ ONLY`
114
+ // unless tidb_enable_noop_functions is on. Detected once at connect time.
115
+ this.supportsReadOnlyTransaction = true;
97
116
  }
98
117
  getId() {
99
118
  return this.sourceId;
@@ -105,8 +124,14 @@ var MariaDBConnector = class _MariaDBConnector {
105
124
  try {
106
125
  const connectionConfig = await this.dsnParser.parse(dsn, config);
107
126
  this.pool = mariadb.createPool(connectionConfig);
108
- await this.pool.query("SELECT 1");
127
+ const rows = await this.pool.query("SELECT VERSION() AS version");
128
+ this.supportsReadOnlyTransaction = !isTiDBVersion(rows?.[0]?.version);
109
129
  } catch (err) {
130
+ if (this.pool) {
131
+ const pool = this.pool;
132
+ this.pool = null;
133
+ await closeQuietly(() => pool.end());
134
+ }
110
135
  console.error("Failed to connect to MariaDB database:", err);
111
136
  throw err;
112
137
  }
@@ -466,46 +491,41 @@ var MariaDBConnector = class _MariaDBConnector {
466
491
  }
467
492
  const conn = await this.pool.getConnection();
468
493
  try {
469
- if (options.readonly) {
470
- await conn.query("START TRANSACTION READ ONLY");
471
- }
472
- let processedSQL = sql;
473
- if (options.maxRows) {
474
- const statements = splitSQLStatements(sql, "mariadb");
475
- const processedStatements = statements.map(
476
- (statement) => SQLRowLimiter.applyMaxRows(statement, options.maxRows)
477
- );
478
- processedSQL = processedStatements.join("; ");
479
- if (sql.trim().endsWith(";")) {
480
- processedSQL += ";";
481
- }
482
- }
483
- let results;
484
- if (parameters && parameters.length > 0) {
485
- try {
486
- results = await conn.query(processedSQL, parameters);
487
- } catch (error) {
488
- console.error(`[MariaDB executeSQL] ERROR: ${error.message}`);
489
- console.error(`[MariaDB executeSQL] SQL: ${processedSQL}`);
490
- console.error(`[MariaDB executeSQL] Parameters: ${JSON.stringify(parameters)}`);
491
- throw error;
494
+ return await withReadOnlyTransaction(
495
+ conn,
496
+ options.readonly,
497
+ this.supportsReadOnlyTransaction,
498
+ async () => {
499
+ let processedSQL = sql;
500
+ if (options.maxRows) {
501
+ const statements = splitSQLStatements(sql, "mariadb");
502
+ const processedStatements = statements.map(
503
+ (statement) => SQLRowLimiter.applyMaxRows(statement, options.maxRows)
504
+ );
505
+ processedSQL = processedStatements.join("; ");
506
+ if (sql.trim().endsWith(";")) {
507
+ processedSQL += ";";
508
+ }
509
+ }
510
+ let results;
511
+ if (parameters && parameters.length > 0) {
512
+ try {
513
+ results = await conn.query(processedSQL, parameters);
514
+ } catch (error) {
515
+ console.error(`[MariaDB executeSQL] ERROR: ${error.message}`);
516
+ console.error(`[MariaDB executeSQL] SQL: ${processedSQL}`);
517
+ console.error(`[MariaDB executeSQL] Parameters: ${JSON.stringify(parameters)}`);
518
+ throw error;
519
+ }
520
+ } else {
521
+ results = await conn.query(processedSQL);
522
+ }
523
+ const rows = parseQueryResults(results);
524
+ const rowCount = extractAffectedRows(results);
525
+ return { rows, rowCount };
492
526
  }
493
- } else {
494
- results = await conn.query(processedSQL);
495
- }
496
- const rows = parseQueryResults(results);
497
- const rowCount = extractAffectedRows(results);
498
- if (options.readonly) {
499
- await conn.query("COMMIT");
500
- }
501
- return { rows, rowCount };
527
+ );
502
528
  } catch (error) {
503
- if (options.readonly) {
504
- try {
505
- await conn.query("ROLLBACK");
506
- } catch {
507
- }
508
- }
509
529
  console.error("Error executing query:", error);
510
530
  throw error;
511
531
  } finally {
@@ -1,13 +1,18 @@
1
1
  import {
2
+ MissingDatabaseError,
2
3
  extractAffectedRows,
3
- parseQueryResults
4
- } from "./chunk-RTB262PR.js";
4
+ isTiDBVersion,
5
+ parseQueryResults,
6
+ requireDatabaseInDSN,
7
+ withReadOnlyTransaction
8
+ } from "./chunk-DUCNPMNW.js";
5
9
  import {
6
10
  quoteIdentifier
7
11
  } from "./chunk-JFWX35TB.js";
8
12
  import {
9
- SQLRowLimiter
10
- } from "./chunk-BXXHWEQS.js";
13
+ SQLRowLimiter,
14
+ closeQuietly
15
+ } from "./chunk-D2BIPC7V.js";
11
16
  import {
12
17
  ConnectorRegistry,
13
18
  SafeURL,
@@ -21,6 +26,8 @@ var MySQLDSNParser = class {
21
26
  async parse(dsn, config) {
22
27
  const connectionTimeoutSeconds = config?.connectionTimeoutSeconds;
23
28
  const timezone = config?.timezone;
29
+ const charset = config?.charset;
30
+ const collation = config?.collation;
24
31
  if (!this.isValidDSN(dsn)) {
25
32
  const obfuscatedDSN = obfuscateDSNPassword(dsn);
26
33
  const expectedFormat = this.getSampleDSN();
@@ -32,11 +39,12 @@ Expected: ${expectedFormat}`
32
39
  }
33
40
  try {
34
41
  const url = new SafeURL(dsn);
42
+ const database = url.pathname ? url.pathname.substring(1) : "";
43
+ requireDatabaseInDSN(database, dsn, "MySQL");
35
44
  const config2 = {
36
45
  host: url.hostname,
37
46
  port: url.port ? parseInt(url.port) : 3306,
38
- database: url.pathname ? url.pathname.substring(1) : "",
39
- // Remove leading '/' if exists
47
+ database,
40
48
  user: url.username,
41
49
  password: url.password,
42
50
  multipleStatements: true,
@@ -61,6 +69,10 @@ Expected: ${expectedFormat}`
61
69
  if (timezone !== void 0) {
62
70
  config2.timezone = timezone;
63
71
  }
72
+ const charsetOrCollation = collation ?? charset;
73
+ if (charsetOrCollation !== void 0) {
74
+ config2.charset = charsetOrCollation;
75
+ }
64
76
  if (url.password && url.password.includes("X-Amz-Credential")) {
65
77
  config2.authPlugins = {
66
78
  mysql_clear_password: () => () => {
@@ -73,6 +85,9 @@ Expected: ${expectedFormat}`
73
85
  }
74
86
  return config2;
75
87
  } catch (error) {
88
+ if (error instanceof MissingDatabaseError) {
89
+ throw error;
90
+ }
76
91
  throw new Error(
77
92
  `Failed to parse MySQL DSN: ${error instanceof Error ? error.message : String(error)}`
78
93
  );
@@ -97,6 +112,9 @@ var MySQLConnector = class _MySQLConnector {
97
112
  this.pool = null;
98
113
  // Source ID is set by ConnectorManager after cloning
99
114
  this.sourceId = "default";
115
+ // TiDB speaks the MySQL protocol but rejects `START TRANSACTION READ ONLY`
116
+ // unless tidb_enable_noop_functions is on. Detected once at connect time.
117
+ this.supportsReadOnlyTransaction = true;
100
118
  }
101
119
  getId() {
102
120
  return this.sourceId;
@@ -111,8 +129,14 @@ var MySQLConnector = class _MySQLConnector {
111
129
  if (config?.queryTimeoutSeconds !== void 0) {
112
130
  this.queryTimeoutMs = config.queryTimeoutSeconds * 1e3;
113
131
  }
114
- const [rows] = await this.pool.query("SELECT 1");
132
+ const [rows] = await this.pool.query("SELECT VERSION() AS version");
133
+ this.supportsReadOnlyTransaction = !isTiDBVersion(rows[0]?.version);
115
134
  } catch (err) {
135
+ if (this.pool) {
136
+ const pool = this.pool;
137
+ this.pool = null;
138
+ await closeQuietly(() => pool.end());
139
+ }
116
140
  console.error("Failed to connect to MySQL database:", err);
117
141
  throw err;
118
142
  }
@@ -472,47 +496,42 @@ var MySQLConnector = class _MySQLConnector {
472
496
  }
473
497
  const conn = await this.pool.getConnection();
474
498
  try {
475
- if (options.readonly) {
476
- await conn.query("START TRANSACTION READ ONLY");
477
- }
478
- let processedSQL = sql;
479
- if (options.maxRows) {
480
- const statements = splitSQLStatements(sql, "mysql");
481
- const processedStatements = statements.map(
482
- (statement) => SQLRowLimiter.applyMaxRows(statement, options.maxRows)
483
- );
484
- processedSQL = processedStatements.join("; ");
485
- if (sql.trim().endsWith(";")) {
486
- processedSQL += ";";
487
- }
488
- }
489
- let results;
490
- if (parameters && parameters.length > 0) {
491
- try {
492
- results = await conn.query({ sql: processedSQL, timeout: this.queryTimeoutMs }, parameters);
493
- } catch (error) {
494
- console.error(`[MySQL executeSQL] ERROR: ${error.message}`);
495
- console.error(`[MySQL executeSQL] SQL: ${processedSQL}`);
496
- console.error(`[MySQL executeSQL] Parameters: ${JSON.stringify(parameters)}`);
497
- throw error;
499
+ return await withReadOnlyTransaction(
500
+ conn,
501
+ options.readonly,
502
+ this.supportsReadOnlyTransaction,
503
+ async () => {
504
+ let processedSQL = sql;
505
+ if (options.maxRows) {
506
+ const statements = splitSQLStatements(sql, "mysql");
507
+ const processedStatements = statements.map(
508
+ (statement) => SQLRowLimiter.applyMaxRows(statement, options.maxRows)
509
+ );
510
+ processedSQL = processedStatements.join("; ");
511
+ if (sql.trim().endsWith(";")) {
512
+ processedSQL += ";";
513
+ }
514
+ }
515
+ let results;
516
+ if (parameters && parameters.length > 0) {
517
+ try {
518
+ results = await conn.query({ sql: processedSQL, timeout: this.queryTimeoutMs }, parameters);
519
+ } catch (error) {
520
+ console.error(`[MySQL executeSQL] ERROR: ${error.message}`);
521
+ console.error(`[MySQL executeSQL] SQL: ${processedSQL}`);
522
+ console.error(`[MySQL executeSQL] Parameters: ${JSON.stringify(parameters)}`);
523
+ throw error;
524
+ }
525
+ } else {
526
+ results = await conn.query({ sql: processedSQL, timeout: this.queryTimeoutMs });
527
+ }
528
+ const [firstResult] = results;
529
+ const rows = parseQueryResults(firstResult);
530
+ const rowCount = extractAffectedRows(firstResult);
531
+ return { rows, rowCount };
498
532
  }
499
- } else {
500
- results = await conn.query({ sql: processedSQL, timeout: this.queryTimeoutMs });
501
- }
502
- const [firstResult] = results;
503
- const rows = parseQueryResults(firstResult);
504
- const rowCount = extractAffectedRows(firstResult);
505
- if (options.readonly) {
506
- await conn.query("COMMIT");
507
- }
508
- return { rows, rowCount };
533
+ );
509
534
  } catch (error) {
510
- if (options.readonly) {
511
- try {
512
- await conn.query("ROLLBACK");
513
- } catch {
514
- }
515
- }
516
535
  console.error("Error executing query:", error);
517
536
  throw error;
518
537
  } finally {
@@ -2,8 +2,9 @@ import {
2
2
  quoteIdentifier
3
3
  } from "./chunk-JFWX35TB.js";
4
4
  import {
5
- SQLRowLimiter
6
- } from "./chunk-BXXHWEQS.js";
5
+ SQLRowLimiter,
6
+ closeQuietly
7
+ } from "./chunk-D2BIPC7V.js";
7
8
  import {
8
9
  ConnectorRegistry,
9
10
  SafeURL,
@@ -148,6 +149,11 @@ var PostgresConnector = class _PostgresConnector {
148
149
  const client = await this.pool.connect();
149
150
  client.release();
150
151
  } catch (err) {
152
+ if (this.pool) {
153
+ const pool = this.pool;
154
+ this.pool = null;
155
+ await closeQuietly(() => pool.end());
156
+ }
151
157
  console.error("Failed to connect to PostgreSQL database:", err);
152
158
  throw err;
153
159
  }
@@ -2,7 +2,7 @@ import {
2
2
  ToolRegistry,
3
3
  getToolRegistry,
4
4
  initializeToolRegistry
5
- } from "./chunk-WPXPKRDU.js";
5
+ } from "./chunk-MQR6VX5F.js";
6
6
  import "./chunk-WVVMH6FJ.js";
7
7
  import "./chunk-JEZZN2YZ.js";
8
8
  export {
@@ -2,8 +2,9 @@ import {
2
2
  quoteIdentifier
3
3
  } from "./chunk-JFWX35TB.js";
4
4
  import {
5
- SQLRowLimiter
6
- } from "./chunk-BXXHWEQS.js";
5
+ SQLRowLimiter,
6
+ closeQuietly
7
+ } from "./chunk-D2BIPC7V.js";
7
8
  import {
8
9
  ConnectorRegistry,
9
10
  SafeURL,
@@ -150,6 +151,11 @@ var SQLiteConnector = class _SQLiteConnector {
150
151
  this.db.exec(initScript);
151
152
  }
152
153
  } catch (error) {
154
+ if (this.db) {
155
+ const db = this.db;
156
+ this.db = null;
157
+ await closeQuietly(() => db.close());
158
+ }
153
159
  console.error("Failed to connect to SQLite database:", error);
154
160
  throw error;
155
161
  }